<!DOCTYPE html><html lang="zh-CN,en,default"><head hexo-theme="https://github.com/volantis-x/hexo-theme-volantis/tree/4.3.1"><meta charset="utf-8"><meta http-equiv="x-dns-prefetch-control" content="on"><link rel="dns-prefetch" href="https://cdn.jsdelivr.net"><link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin><meta name="renderer" content="webkit"><meta name="force-rendering" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"><meta name="HandheldFriendly" content="True"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><link rel="preload" href="/css/first.css" as="style"><title>Redis学习笔记 - Tang&#39;s blog.</title><meta name="keywords" content="Redis"><meta name="description" content="计算机就是数据结构和算法."><link rel="alternate" href="/atom.xml" title="Tang's blog." type="application/atom+xml"><meta name="msapplication-TileColor" content="#ffffff"><meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/blog/favicon/browserconfig.xml"><meta name="theme-color" content="#ffffff"><link rel="apple-touch-icon" sizes="180x180" href="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/favicon/cf_1o_180x180.png"><link rel="icon" type="image/png" sizes="32x32" href="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/favicon/cf_1o_32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/favicon/cf_1o_16x16.png"><link rel="manifest" href="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/favicon/manifest2.json"><link rel="mask-icon" href="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/favicon/cf_image2vector.svg" color="#5bbad5"><link rel="shortcut icon" href="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/favicon/32favicon32.ico"><link rel="stylesheet" href="/css/first.css"><link rel="stylesheet" href="/css/style.css" media="print" onload='this.media="all",this.onload=null'><noscript><link rel="stylesheet" href="/css/style.css"></noscript><script id="loadcss"></script><script>window.MSInputMethodContext&&document.documentMode&&document.write('<style>html{overflow-x: hidden !important;overflow-y: hidden !important;}.kill-ie{text-align:center;height: 100%;margin-top: 15%;margin-bottom: 5500%;}</style><div class="kill-ie"><h1><b>抱歉，您的浏览器无法访问本站</b></h1><h3>微软已经于2016年终止了对 Internet Explorer (IE) 10 及更早版本的支持，<br/>继续使用存在极大的安全隐患，请使用当代主流的浏览器进行访问。</h3><br/><a target="_blank" rel="noopener external nofollow noreferrer" href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support"><strong>了解详情 ></strong></a></div>')</script><noscript><style>html{overflow-x:hidden!important;overflow-y:hidden!important}.kill-noscript{text-align:center;height:100%;margin-top:15%;margin-bottom:5500%}</style><div class="kill-noscript"><h1><b>抱歉，您的浏览器无法访问本站</b></h1><h3>本页面需要浏览器支持（启用）JavaScript</h3><br> <a target="_blank" rel="noopener external nofollow noreferrer" href="https://www.baidu.com/s?wd=启用JavaScript"><strong>了解详情 ></strong></a></div></noscript></head><body><header id="l_header" class="l_header always shadow blur show"><div class="container"><div id="wrapper"><div class="nav-sub"><p class="title"></p><ul class="switcher nav-list-h m-phone" id="pjax-header-nav-list"><li><a id="s-comment" class="fas fa-comments fa-fw" target="_self" href="javascript:void(0)" rel="external nofollow noreferrer"></a></li><li><a id="s-toc" class="s-toc fas fa-list fa-fw" target="_self" href="javascript:void(0)" rel="external nofollow noreferrer"></a></li></ul></div><div class="nav-main"> <a class="title flat-box" target="_self" href="/"><img no-lazy class="logo" src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/cover/fav/cf_o1x64.jpg"></a><div class="menu navigation"><ul class="nav-list-h m-pc"><li><a class="menuitem flat-box faa-parent animated-hover" href="/" id="home"><i class="fas fa-home fa-fw"></i> Home</a></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-th-list fa-fw"></i> Articles</a><ul class="list-v"><li><a class="menuitem flat-box faa-parent animated-hover" href="/categories/" id="categories"><i class="fas fa-folder-open fa-fw"></i> Categories</a></li><li><a class="menuitem flat-box faa-parent animated-hover" href="/tags/" id="tags"><i class="fas fa-tags fa-fw"></i> Tags</a></li><li><a class="menuitem flat-box faa-parent animated-hover" href="/archives/" id="archives"><i class="fas fa-archive fa-fw"></i> Archives</a></li></ul></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-fan faa-spin animated fa-fw"></i> Play</a><ul class="list-v"><li><a class="menuitem flat-box faa-parent animated-hover" href="/catchTheCat/" id="catchTheCat"><i class="fas fa-cat faa-wrench animated-hover fa-fw"></i> Chat Noir</a></li></ul></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-cog fa-spin fa-fw"></i> Tools</a><ul class="list-v"><li><a class="menuitem flat-box header toggle-mode-btn"><i class="fas fa-moon fa-fw"></i> 暗黑模式</a></li><li></li><li><a class="menuitem flat-box"><i class="fas fa-compact-disc fa-fw music"></i> Music</a><ul class="list-v"><li><div class="aplayer-container"><meting-js theme="#1BCDFC" autoplay="false" volume="0.7" loop="all" order="list" fixed="false" list-max-height="320px" server="netease" type="playlist" id="5472211102" list-folded="true"></meting-js></div></li></ul></li><li></li></ul></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-info-circle fa-fw"></i> Me</a><ul class="list-v"><li><a class="menuitem flat-box faa-parent animated-hover" href="/about/" id="about"><i class="fas fa-ellipsis-v fa-fw"></i> About</a></li><li><a class="menuitem flat-box faa-parent animated-hover" href="https://aner1001.gitee.io/resume/" target="_blank" id="https:aner1001giteeioresume"><i class="fas fa-clock fa-fw"></i> Resume</a></li></ul></li></ul></div><div id="tp-weather-widget"></div><script>!function(e,t,n,a,i,o,c,r){r=function(){o=t.createElement(n),c=t.getElementsByTagName(n)[0],o.src=i,o.charset="utf-8",o.async=1,c.parentNode.insertBefore(o,c)},e.SeniverseWeatherWidgetObject=a,e[a]||(e[a]=function(){(e[a].q=e[a].q||[]).push(arguments)}),e[a].l=+new Date,e.attachEvent?e.attachEvent("onload",r):e.addEventListener("load",r,!1)}(window,document,"script","SeniverseWeatherWidget","//cdn.sencdn.com/widget2/static/js/bundle.js?t="+parseInt(((new Date).getTime()/1e8).toString(),10)),window.SeniverseWeatherWidget("show",{flavor:"slim",location:"WTW3SJ5ZBJUY",geolocation:!0,language:"zh-Hans",unit:"c",theme:"auto",token:"563a4097-8461-469b-ac30-123bac0010d1",hover:"enabled",container:"tp-weather-widget"})</script><div class="m_search"><form name="searchform" class="form u-search-form"><i class="icon fas fa-search fa-fw"></i> <input type="text" class="input u-search-input" placeholder="Search..."></form></div><ul class="switcher nav-list-h m-phone"><li><a class="s-search fas fa-search fa-fw" target="_self" href="javascript:void(0)" rel="external nofollow noreferrer"></a></li><li><a class="s-menu fas fa-bars fa-fw" target="_self" href="javascript:void(0)" rel="external nofollow noreferrer"></a><ul class="menu-phone list-v navigation white-box"><li><a class="menuitem flat-box faa-parent animated-hover" href="/" id="home"><i class="fas fa-home fa-fw"></i> Home</a></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-th-list fa-fw"></i> Articles</a><ul class="list-v"><li><a class="menuitem flat-box faa-parent animated-hover" href="/categories/" id="categories"><i class="fas fa-folder-open fa-fw"></i> Categories</a></li><li><a class="menuitem flat-box faa-parent animated-hover" href="/tags/" id="tags"><i class="fas fa-tags fa-fw"></i> Tags</a></li><li><a class="menuitem flat-box faa-parent animated-hover" href="/archives/" id="archives"><i class="fas fa-archive fa-fw"></i> Archives</a></li></ul></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-fan faa-spin animated fa-fw"></i> Play</a><ul class="list-v"><li><a class="menuitem flat-box faa-parent animated-hover" href="/catchTheCat/" id="catchTheCat"><i class="fas fa-cat faa-wrench animated-hover fa-fw"></i> Chat Noir</a></li></ul></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-cog fa-spin fa-fw"></i> Tools</a><ul class="list-v"><li><a class="menuitem flat-box header toggle-mode-btn"><i class="fas fa-moon fa-fw"></i> 暗黑模式</a></li><li></li></ul></li><li><a class="menuitem flat-box faa-parent animated-hover"><i class="fas fa-info-circle fa-fw"></i> Me</a><ul class="list-v"><li><a class="menuitem flat-box faa-parent animated-hover" href="/about/" id="about"><i class="fas fa-ellipsis-v fa-fw"></i> About</a></li><li><a class="menuitem flat-box faa-parent animated-hover" href="https://aner1001.gitee.io/resume/" target="_blank" id="https:aner1001giteeioresume"><i class="fas fa-clock fa-fw"></i> Resume</a></li></ul></li></ul></li></ul></div></div></div></header><div id="l_body"><div id="l_cover"><div id="full" class="cover-wrapper post blank" style="display:none"><div id="cover-backstretch"></div><div id="scroll-down" style="display:none"><i class="fa fa-chevron-down scroll-down-effects"></i></div></div></div><div id="safearea"><div class="body-wrapper" id="pjax-container"><div class="l_main"><article class="article post white-box reveal md shadow article-type-post" id="post" itemscope itemprop="blogPost"><div class="article-meta" id="top"><h1 class="title"> Redis学习笔记</h1><div class="new-meta-box"><div class="new-meta-item author"> <a class="author" href="/" rel="nofollow"><img no-lazy src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/img/cover/fav/cf_1o_x16.jpg"><p>汤晔飞</p></a></div><div class="new-meta-item category"><a class="notlink"><i class="fas fa-folder-open fa-fw" aria-hidden="true"></i> <a class="category-link" href="/categories/%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86/">技术知识</a><span class="sep"></span><a class="category-link" href="/categories/%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86/%E6%95%B0%E6%8D%AE%E5%BA%93/">数据库</a></a></div><div class="new-meta-item date"><a class="notlink"><i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i><p>发布于：2021年7月10日</p></a></div><div class="new-meta-item browse leancloud"><a class="notlink"><div id="lc-pv" data-title="Redis学习笔记" data-path="/posts/2021/07/10/10f923e8abfb/"><i class="fas fa-eye fa-fw" aria-hidden="true"></i><span id="number"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 次浏览</div></a></div></div></div><p>计算机就是数据结构和算法.</p><span id="more"></span><p>redis的学习网站：</p><ol><li><p><a target="_blank" rel="noopener external nofollow noreferrer" href="http://redis.cn">redis.cn</a></p></li><li><p><a target="_blank" rel="noopener external nofollow noreferrer" href="http://redis.io">redis.io</a></p></li><li><p><a target="_blank" rel="noopener external nofollow noreferrer" href="http://db-engines.com">db-engines.com</a></p></li></ol><p>API代码的学习：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">1.  redis.io 的client 中有JAVA语言的客户端：jedis、lettuce等可以分别访问他们的github学习</span><br><span class="line">2.  另外是基于spring的使用，主动通过spring.io官网学习spring.data.redis</span><br><span class="line">3.  spring.io中:   https://spring.io/projects/spring-data-redis</span><br></pre></td></tr></table></figure><h2 id="基础概念"><a class="header-anchor" href="#基础概念">☀</a>基础概念</h2><h3 id="为什么要使用Redis"><a class="header-anchor" href="#为什么要使用Redis">☀</a>为什么要使用Redis</h3><p>文件里 数据 data.txt，当查询某个字段的时候<code>grep awk</code>，java但是随着文件变大，速度变慢为什么？<br> 因为：硬盘i/o成为瓶颈。查询有一个过程，取出比较。主要取决于取出的速度。</p><blockquote><p><strong>💖常识：</strong></p><p>磁盘：</p><ol><li><p>寻址：ms</p></li><li><p>带宽：G/M</p></li></ol><p>内存：</p><ol><li><p>寻址：ns</p></li><li><p>带宽：很大</p><p>秒&gt;毫秒&gt;微秒&gt;纳秒 磁盘比内存在寻址上慢了10W倍</p></li></ol><p>I/O buffer：成本问题</p><p>磁盘与磁道，扇区，一扇区 512Byte带来一个成本变大：索引</p><p>windows操作系统，一个扇区4K，4K 操作系统，无论你读多少，都是最少4k从磁盘拿，4K对其。</p></blockquote><blockquote><p><strong>😘关系型数据库特性</strong></p><p>磁盘中:数据+索引，内存中B+T树。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313160025.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313160025.png" srcset="" alt="image-20210313160025961"></p><p>关系型数据库建表：必须给出schema</p><p>类型：字节宽度。</p><p>存：倾向于行级存储。</p><p><strong>(✿◡‿◡)面试题：</strong></p><p>数据库：表很大，性能下降？</p><p>如果表有索引</p><p>维护索引会使得，增删改变慢。</p><p>查询速度呢？</p><ol><li><p>1个或少量查询依然很快。</p></li><li><p>并发大的时候会受硬盘带宽影响速度。</p></li></ol></blockquote><blockquote><p>💕<strong>技术选型</strong></p><p>数据库对比网站：<a target="_blank" rel="noopener external nofollow noreferrer" href="https://db-engines.com/en/">https://db-engines.com/en/</a></p><p>架构师：</p><ol><li><strong>技术选型</strong></li></ol></blockquote><blockquote><p>数据在磁盘和内存体积不一样，SAPHANA内存级别的关系型数据库2T，这种造价太高。</p><p>取折中的方案，使用缓存</p><ol><li><p>memcached</p></li><li><p>redis</p></li></ol><p>速度主要是由个基础设施影响：</p><ol><li><p>冯诺依曼体系的硬件</p></li><li><p>以太网，tcp/ip的网络</p></li></ol></blockquote><h3 id="Redis简介"><a class="header-anchor" href="#Redis简介">☀</a>Redis简介</h3><blockquote><p>Redis 是一个开源（BSD许可）的，内存中的数据结构存储系统，它可以用作数据库、缓存和消息中间件。</p><p>它支持多种类型的数据结构，如 字符串（strings）， 散列（hashes）， 列表（lists）， 集合（sets）， 有序集合（sorted sets） 与范围查询， bitmaps， hyperloglogs 和 地理空间（geospatial） 索引半径查询。</p><p>Redis 内置了 复制（replication），LUA脚本（Lua scripting）， LRU驱动事件（LRU eviction），事务（transactions） 和不同级别的 磁盘持久化（persistence）， 并通过 Redis哨兵（Sentinel）和自动 分区（Cluster）提供高可用性（high availability）。</p><p>😎<strong>Redis和memcache的区别</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313162711.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313162711.png" srcset="" alt="image-20210313162711212"></p><p>总结一句话：计算像数据移动。</p></blockquote><h3 id="字符集的的使用"><a class="header-anchor" href="#字符集的的使用">☀</a>字符集的的使用</h3><blockquote><p>字符集 ascii，其他一般叫做扩展字符集。</p><p>扩展： 其他字符集不在对ascii重编码。</p><p>0xxxxxxx，以0开头的肯定是ascii编码，其他字符集一遇到就默认使用ascii了。</p><p>你自己写一个程序，字节流读取，每字节判断。</p></blockquote><h2 id="Redis安装"><a class="header-anchor" href="#Redis安装">☀</a>Redis安装</h2><p><strong>😊命令执行过程</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">1，yum install wget</span><br><span class="line">2,<span class="built_in">cd</span> ~</span><br><span class="line">3,<span class="built_in">mkdir</span> soft</span><br><span class="line">4,<span class="built_in">cd</span> soft</span><br><span class="line">5,wget    http://download.redis.io/releases/redis-5.0.5.tar.gz</span><br><span class="line">6,tar xf    redis...tar.gz</span><br><span class="line">7,<span class="built_in">cd</span> redis-src</span><br><span class="line">8,看README.md</span><br><span class="line">9, make</span><br><span class="line"> ....yum install  gcc  </span><br><span class="line"> ....make distclean</span><br><span class="line">10,make</span><br><span class="line">11,<span class="built_in">cd</span> src   ....生成了可执行程序</span><br><span class="line">12, <span class="built_in">cd</span> ..</span><br><span class="line">13,make install PREFIX=/usr/local/redis</span><br><span class="line">14,vi /etc/profile</span><br><span class="line">...   <span class="built_in">export</span>  REDIS_HOME=/opt/mashibing/redis5</span><br><span class="line">...   <span class="built_in">export</span> PATH=<span class="variable">$PATH</span>:<span class="variable">$REDIS_HOME</span>/bin</span><br><span class="line">..<span class="built_in">source</span> /etc/profile</span><br><span class="line">15,<span class="built_in">cd</span> utils</span><br><span class="line">16,./install_server.sh  （可以执行一次或多次）</span><br><span class="line">    a)  一个物理机中可以有多个redis实例（进程），通过port区分</span><br><span class="line">    b)  可执行程序就一份在目录，但是内存中未来的多个实例需要各自的配置文件，持久化目录等资源</span><br><span class="line">    c)  service   redis_6379  start/stop/stauts     &gt;   linux   /etc/init.d/****</span><br><span class="line">    d)脚本还会帮你启动！</span><br><span class="line">    f) /etc/redis/   目录中会生成redis启动的配置文件，以后也可以通过，redis-server 搭配此配置文件进行服务的启动。</span><br><span class="line">17,ps -fe |  grep redis  </span><br></pre></td></tr></table></figure><h2 id="卸载Redis"><a class="header-anchor" href="#卸载Redis">☀</a>卸载Redis</h2><p><code>find / -name redis</code></p><p>查找redis的文件目录，删除即可。</p><h2 id="常用命令"><a class="header-anchor" href="#常用命令">☀</a>常用命令</h2><h3 id="查看Redis"><a class="header-anchor" href="#查看Redis">☀</a>查看Redis</h3><p>查看Redis的bin被安装位置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">whereis redis-cli</span><br><span class="line">whereis redis-server</span><br></pre></td></tr></table></figure><p>=======<br> 测试结果，只会找配置在环境变量中的目录下的。</p><p>查看Redis的进程：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ps -ef |grep redis</span><br></pre></td></tr></table></figure><p>查看Redis版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./redis-server -v</span><br></pre></td></tr></table></figure><h3 id="登录Redis"><a class="header-anchor" href="#登录Redis">☀</a>登录Redis</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./redis-cli -h IP地址 -p 端口 -a 密码  -c  --raw</span><br></pre></td></tr></table></figure><table><thead><tr><th>参数</th><th>描述</th></tr></thead><tbody><tr><td><code>-c</code></td><td>开启reidis cluster模式，连接redis cluster节点时候使用。</td></tr><tr><td><code>-p</code></td><td>端口号，不写此参数就是用默认端口</td></tr><tr><td><code>-h</code></td><td>ip地址，不写，默认本机。</td></tr><tr><td><code>--raw</code></td><td>使用客户端的指定的字符集进行解码显示。</td></tr></tbody></table><h3 id="启动Redis"><a class="header-anchor" href="#启动Redis">☀</a>启动Redis</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#如果在服务目录/etc/init.d/****中注册了脚本.</span></span><br><span class="line">service redis_6379 start</span><br><span class="line"></span><br><span class="line"><span class="comment">#使用客户端命令的启动方式，搭配配置文件即可。</span></span><br><span class="line">./redis-server   /etc/redis/6379.confg</span><br><span class="line"></span><br><span class="line"><span class="comment">#如果配置了环境变量,手工启动测试,会将当前启动的目录作为,持久化目录,所以必须先创建目录.</span></span><br><span class="line">redis-server --port 6379</span><br></pre></td></tr></table></figure><table><thead><tr><th>参数</th><th>描述</th></tr></thead><tbody><tr><td><code>--loadmodule</code></td><td>加载模块启动服务</td></tr></tbody></table><h3 id="停止Redis"><a class="header-anchor" href="#停止Redis">☀</a>停止Redis</h3><p><strong>强制停止法：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ps -ef | grep redis  <span class="comment">#查看端口占用的进程</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">kill</span> -9  进程的pid    <span class="comment">#强制停止进程</span></span><br></pre></td></tr></table></figure><p><strong>登录客户端停止法：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 登录命令</span></span><br><span class="line">./redis-cli ......</span><br><span class="line"><span class="comment">#停止</span></span><br><span class="line">shoutdown</span><br><span class="line"></span><br><span class="line"><span class="comment">#或者联合起来使用</span></span><br><span class="line">./redis-cli -p 6379 shutdown</span><br></pre></td></tr></table></figure><p><strong>服务停止法：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service redis_6379 stop</span><br></pre></td></tr></table></figure><h3 id="查看Redis配置："><a class="header-anchor" href="#查看Redis配置：">☀</a>查看Redis配置：</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis-cli ...  <span class="comment">#登录redis</span></span><br><span class="line">CONFIG GET *   <span class="comment">#获取配置。</span></span><br></pre></td></tr></table></figure><h2 id="conf配置文件"><a class="header-anchor" href="#conf配置文件">☀</a><strong>conf配置文件</strong></h2><p>引入公共的配置：<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318092941.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318092941.png" srcset="" alt="image-20210318092941190"></p><p>加载模块配置<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093014.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093014.png" srcset="" alt="image-20210318093014144"></p><p>绑定能够访问的IP地址<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093349.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093349.png" srcset="" alt="image-20210318093349733"></p><p>默认是本机。如果需要在虚拟机外部的程序访问需要修改为：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">bind</span> 0.0.0.0</span><br></pre></td></tr></table></figure><p>是否允许外部主机访问。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093445.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093445.png" srcset="" alt="image-20210318093445106"></p><p>是否为后台服务模式<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093643.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093643.png" srcset="" alt="image-20210318093643043"></p><p>启动后，进程的pid文件。放在这个目录下。通过pid文件判定进程是否在运行。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093752.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093752.png" srcset="" alt="image-20210318093752634"></p><p>日志级别<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093952.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318093952.png" srcset="" alt="image-20210318093937957"></p><p>日志存放在那个目录下：<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094051.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094051.png" srcset="" alt="image-20210318094051461"></p><p>库的数量：<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094218.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094218.png" srcset="" alt="image-20210318094218580"></p><p>需要密码登录：<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094408.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094408.png" srcset="" alt="image-20210318094408735"></p><p>将一些敏感的命令重命名，屏蔽。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094542.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094542.png" srcset="" alt="image-20210318094542304"></p><p>允许最大连接数：<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094657.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094657.png" srcset="" alt="image-20210318094657329"></p><p>最大内存，根据经验一般是1G~10G。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094916.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/17/20210318094916.png" srcset="" alt="image-20210318094916390"></p><h2 id="内核演变过程"><a class="header-anchor" href="#内核演变过程">☀</a>内核演变过程</h2><h3 id="演变"><a class="header-anchor" href="#演变">☀</a>演变</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313164359.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313164359.png" srcset="" alt="image-20210313164359128"></p><blockquote><p><strong>🤢阻塞时期：</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313164448.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313164448.png" srcset="" alt="image-20210313164448116"></p><p>cpu只有1颗</p><p>JVM: 一个线程的成本 1MB</p><ol><li><p>线程多了调度成本CPU浪费。</p></li><li><p>内存成本。</p></li></ol></blockquote><blockquote><p>🤔<strong>非阻塞时期</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313164726.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313164726.png" srcset="" alt="image-20210313164726540"></p><p>同步非阻塞 NIO</p><p>如果有1000fd，代表用户进程轮询调用1000次kernel，成本问题。</p></blockquote><blockquote><p>😃<strong>select，多路复用NIO时期。</strong></p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313165341.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313165341.png" srcset="" alt="image-20210313165341855"></p><p>fd相关数据考来考去。数据拷贝和传参调用成本比较高。</p></blockquote><blockquote><p><strong>😎epoll，多路复用NIO时期。</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313165629.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313165629.png" srcset="" alt="image-20210313165629053"></p><p>共享空间mmap，降低了传参调用。</p></blockquote><blockquote><p><strong>😍🤷‍♀️mmap，sendfile时期。</strong></p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313170729.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313170729.png" srcset="" alt="image-20210313170729325"></p><p>也就是现在的卡夫卡(kafka)技术的出现。减少数据拷贝和传参调用。</p></blockquote><h3 id="Redis内核"><a class="header-anchor" href="#Redis内核">☀</a>Redis内核</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313172750.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313172750.png" srcset="" alt="image-20210313172750644"></p><p>redis的每个服务端都是，单进程，单线程，单实例。</p><p>单线程单进程的好处：“顺序”性，<strong>每连接内</strong>的命令顺序。</p><h2 id="Value结构和操作命令："><a class="header-anchor" href="#Value结构和操作命令：">☀</a>Value结构和操作命令：</h2><p>客户端<code>help</code>命令可以查看所有命令的用法。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313173926.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313173926.png" srcset="" alt="image-20210313173926024"></p><h3 id="字符串"><a class="header-anchor" href="#字符串">☀</a>字符串</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313173204.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313173204.png" srcset="" alt="image-20210313173204325"></p><p>redis所有存储的都是字节流。如果时字符串，取决于客户端的编码格式，所以一定要统一编码。</p><p>每一个字节，都存在正反向索引：<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313191623.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313191623.png" srcset="" alt="image-20210313191623242"></p><blockquote><p><strong><code>set</code></strong></p><p>设置值。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313174533.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313174533.png" srcset="" alt="image-20210313174158906"></p></blockquote><blockquote><p><strong><code>get</code></strong></p><p>获取key的值。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313174541.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313174541.png" srcset="" alt="image-20210313174245296"></p></blockquote><blockquote><p><strong><code>append</code></strong></p><p>字符串追加。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175015.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175015.png" srcset="" alt="image-20210313174527895"><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175013.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175013.png" srcset="" alt="image-20210313174624584"></p></blockquote><blockquote><p><strong><code>setrange</code></strong></p><p>从某个位置开始覆盖替换字符串。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313174954.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313174954.png" srcset="" alt="image-20210313174954008"></p></blockquote><blockquote><p><strong><code>getrange</code></strong></p><p>获取一段字符串，从字节位置开始。所以用来取中文就不太方便了。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175510.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175510.png" srcset="" alt="image-20210313175510267"></p></blockquote><blockquote><p><strong><code>strlen</code></strong></p><p>获取字节数量意义上的长度。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175901.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313175901.png" srcset="" alt="image-20210313175901455"></p></blockquote><h3 id="数值"><a class="header-anchor" href="#数值">☀</a>数值</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313180413.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313180413.png" srcset="" alt="image-20210313180413871"></p><blockquote><p><strong><code>incr</code></strong></p><p>从原来的数上+1.</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313180435.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313180435.png" srcset="" alt="image-20210313180326263"></p><p>用途场景：<br> 抢购，秒杀，详情页，点赞，评论<br> 规避并发下，<br> 对数据库的事务操作<br> 完全由redis内存操作代替</p></blockquote><h3 id="bitmap"><a class="header-anchor" href="#bitmap">☀</a>bitmap</h3><p>🤷‍♀️直接操作二进制位，牛逼。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313180832.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313180832.png" srcset="" alt="image-20210313180832154"></p><blockquote><p><strong><code>setbit</code></strong></p><p>针对某个二进制位进行设置（0/1）：<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313181505.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313181505.png" srcset="" alt="image-20210313181505822"><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313190454.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313190454.png" srcset="" alt="image-20210313190454924"></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setbit k1 1 1</span><br><span class="line">setbit k1 7 1</span><br><span class="line">setbit k1 9 1</span><br></pre></td></tr></table></figure><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313191121.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313191121.png" srcset="" alt="image-20210313191121942"></p></blockquote><blockquote><p><strong><code>bitcount</code></strong></p><p>根据字节角标，计算二进制位<code>1</code>存在的数量。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313181935.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313181935.png" srcset="" alt="image-20210313181935487"></p></blockquote><blockquote><p><strong><code>bitpos</code></strong></p><p>根据字节角标，命令返回字符串里面第一个被设置为1或者0的bit位。出现的起始位置坐标。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313184144.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313184144.png" srcset="" alt="image-20210313184144821"></p><p>🐱‍👤注意：需要指定查询出现的0或者1。</p><p><code>BITPOS k1 0 0</code></p><p>第一个参数<code>0</code>指定查询0</p><p>第二个参数是<code>0</code>指定从第0个字节角标开始。</p></blockquote><blockquote><p><strong><code>bitop</code></strong></p><p>将多个bitmap进行逻辑计算操作，并将结果存入新的bitmap中。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313185329.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313185329.png" srcset="" alt="image-20210313185329239"></p></blockquote><h4 id="使用场景"><a class="header-anchor" href="#使用场景">☀</a>使用场景</h4><ol><li><p>有用户系统，统计用户登录天数，且窗口随机.</p><p>😏<strong>实现思路</strong>：用户id作为key，将二进制位<code>1</code>存储表示已登陆的实际第几天。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">setbit sean 1 1</span><br><span class="line">setbit sean 7 1</span><br><span class="line">setbit sean 364 1</span><br><span class="line">STRLEN sean</span><br><span class="line">BITCOUNT sean -2 -1</span><br></pre></td></tr></table></figure></li><li><p>京东就是你们的，618做活动：送礼物，大库备货多少礼物？</p><p>假设京东有2E用户</p><p>僵尸用户</p><p>冷热用户/忠诚用户</p><p>活跃用户统计！随即窗口</p><p>比如说 1号~3号 连续登录要 去重</p><p>😏实现思路：天数作为key，二进制位代表实际用户，已登录，用<code>1</code>表示。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">setbit 20190101  1 1</span><br><span class="line">setbit 20190102  1 1</span><br><span class="line">setbit 20190102  7 1</span><br><span class="line">bitop or destkey 20190101 20190102</span><br><span class="line">BITCOUNT destkey 0 -1</span><br></pre></td></tr></table></figure></li></ol><h3 id="List"><a class="header-anchor" href="#List">☀</a>List</h3><p>链表结构，存在争对元素的正负索引。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313192511.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313192511.png" srcset="" alt="image-20210313192511217"></p><p>😁<strong>口诀</strong>：同向命令模拟栈操作，逆向命令模拟队列操作。</p><blockquote><p><strong><code>lpush</code></strong></p><p>从左向右存入元素。</p><p><strong><code>lpop</code></strong></p><p>按从左向右存入的顺序，弹出最后存入的数据。</p><p><code>lpush</code>+<code>lpop</code> 同向命令模拟栈的使用。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313194523.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313194523.png" srcset="" alt="image-20210313194522973"></p></blockquote><blockquote><p><strong><code>rpush</code></strong></p><p>从右向左存入元素</p><p><strong><code>rpop</code></strong></p><p>按从右向左存入的顺序，弹出最后存入的数据。</p><p><code>lpush</code>+<code>rpop</code>逆向命令模拟队列的数据结构。</p></blockquote><blockquote><p><strong><code>LRANGE</code></strong></p><p>按照给定的范围，取出list中的值。<code>lpush</code>的list是栈取出。<code>rpush</code>的list是队列取出。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313195221.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313195221.png" srcset="" alt="image-20210313195221779"></p></blockquote><blockquote><p><strong><code>LINDEX</code></strong></p><p>按照索引取出值。操作索引就相当于数组结构了。</p><p><img src="https://i.loli.net/2021/04/07/wQUGOItCmhBg64s.png" class="lazyload" data-srcset="https://i.loli.net/2021/04/07/wQUGOItCmhBg64s.png" srcset="" alt="image-20210407194644342"></p></blockquote><blockquote><p><strong><code>LSET</code></strong></p><p>按照索引设置对于的某个值.</p></blockquote><blockquote><p><strong><code>LREM</code></strong></p><p>从list中,按照给定的数量,移除某个值.数量为正数,正向移除,为负数,逆向移除.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313203240.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313203240.png" srcset="" alt="image-20210313203240738"></p></blockquote><blockquote><p><strong><code>LINSERT</code></strong></p><p>在list中的某个元素前后插入元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313203610.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313203610.png" srcset="" alt="image-20210313203610647"></p></blockquote><blockquote><p><strong><code>llen</code></strong></p><p>统计有多少个元素。</p></blockquote><blockquote><p><strong><code>blpop</code> <code>brpop</code></strong></p><p>阻塞弹出元素。实现消息的单播队列。谁先阻塞，谁先拿到元素。FIFO，先进先出。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313203928.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313203928.png" srcset="" alt="image-20210313203928005"></p><p>开启窗口1 2 3 。</p><p>窗口1先进入阻塞，等待k1弹出。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204127.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204127.png" srcset="" alt="image-20210313204127871"></p><p>窗口2后进入阻塞，等待k2弹出。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204136.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204136.png" srcset="" alt=""></p><p>窗口3，压入元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204301.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204301.png" srcset="" alt="image-20210313204301276"></p><p>窗口1，先收到弹出的元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204338.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204338.png" srcset="" alt="image-20210313204338574"></p><p>窗口3，再压入元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204426.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204426.png" srcset="" alt="image-20210313204426379"></p><p>窗口2，收到弹出的元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204545.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204545.png" srcset="" alt="image-20210313204545509"></p></blockquote><blockquote><p><strong><code>LTRIM</code></strong></p><p>对两端外的数据进行移除和删除。不包括端的元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204616.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204616.png" srcset="" alt="image-20210313204616252"></p></blockquote><h3 id="Hash"><a class="header-anchor" href="#Hash">☀</a>Hash</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204918.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313204918.png" srcset="" alt="image-20210313204918924"></p><blockquote><p><strong><code>hset</code></strong></p><p>存hash，键值对类型的数据。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313213236.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313213236.png" srcset="" alt="image-20210313213236238"></p></blockquote><blockquote><p><strong><code>hmset</code></strong></p><p>在一个key的value中，存放多个，键值对类型数据。</p></blockquote><blockquote><p><strong><code>hget</code></strong></p><p>取出</p></blockquote><blockquote><p><strong><code>hmget</code></strong></p><p>根据key的values中根据多个键取值。</p></blockquote><blockquote><p><strong><code>hkeys</code></strong></p><p>取出所有的键。</p></blockquote><blockquote><p><strong><code>hvals</code></strong></p><p>取出所有的值。</p></blockquote><blockquote><p><strong><code>hgetall</code></strong></p><p>取出所有的键和值。</p></blockquote><blockquote><p><strong><code>hincrbyfloat</code></strong></p><p>将某个map中的值进行浮点数计算。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313213801.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210313213801.png" srcset="" alt="image-20210313213801505"></p></blockquote><h3 id="Set"><a class="header-anchor" href="#Set">☀</a>Set</h3><p>最有优势的就是去重。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071703.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071703.png" srcset="" alt="image-20210314071703405"></p><blockquote><p><strong><code>sadd</code></strong></p><p>集合中添加元素.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314065733.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314065733.png" srcset="" alt="image-20210314065726468"></p></blockquote><blockquote><p><strong><code>smembers</code></strong></p><p>显示集合中的元素.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314070835.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314070835.png" srcset="" alt="image-20210314070835592"></p></blockquote><blockquote><p><strong><code>sinter</code></strong></p><p>做多个集合的交集,不存放结果.,通过io将结果输出.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071107.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071107.png" srcset="" alt="image-20210314071107943"></p></blockquote><blockquote><p><strong><code>sinterstore</code></strong></p><p>做多个集合的交集,存放结果.没有io的交互.​👓​<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071355.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071355.png" srcset="" alt="image-20210314071355655"></p></blockquote><blockquote><p><strong>sunion</strong></p><p>做并集</p></blockquote><blockquote><p><strong>sunionstore</strong></p><p>做并集并保存.</p></blockquote><blockquote><p><strong>sdiff</strong></p><p>差集.以第一个元素为基础取差集。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071526.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314071526.png" srcset="" alt="image-20210314071526720"></p></blockquote><blockquote><p><strong><code>srandmember</code></strong></p><p>随机数<br> SRANDMEMBER key count<br> 正数：取出一个去重的结果集（不能超过已有集），超过已有集数量返回整个set集合。<br> 负数：取出一个带重复的结果集，一定满足你要的数量。<br> 如果：0，不返回<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314072014.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314072014.png" srcset="" alt="image-20210314072014223"><br> <strong>🎑应用场景：</strong></p><ol><li><p>抽奖：<br> 10个奖品<br> 用户：<br> &lt;10 使用负数抽取。<br> &gt;10 使用正数抽取。<br> 中奖：是否重复</p></li><li><p>解决家庭争斗！<br> 取名字的时候。5个名字，取10次(可重复)</p></li></ol></blockquote><blockquote><p><strong><code>spop</code></strong></p><p>将集合元素随机弹出。每次只弹出一个。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314072729.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314072729.png" srcset="" alt="image-20210314072729670"></p></blockquote><h3 id="sorted-set"><a class="header-anchor" href="#sorted-set">☀</a>sorted_set</h3><p>理解排序的集合中，内部存储方式。对每个元素内置了一个分值，按照分值来排序的。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314075256.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314075256.png" srcset="" alt="image-20210314075256639"></p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314075228.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314075228.png" srcset="" alt="image-20210314075228310"></p><p>🎆**面试问题：**排序是怎么实现的，增删改查的速度？</p><p>​ 答：使用了跳跃表的概念。<strong>吸收存储空间，换取未来的查询速度。</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314110258.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314110258.png" srcset="" alt="image-20210314110258844"></p><blockquote><p><strong><code>zadd</code></strong></p><p>集合中添加元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314075706.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314075706.png" srcset="" alt="image-20210314075706277"></p></blockquote><blockquote><p><strong><code>zrange</code></strong></p><p>取出集合中的元素，按照元素角标顺序。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314080759.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314080759.png" srcset="" alt="image-20210314080759460"></p></blockquote><blockquote><p><strong><code>zrangebyscore</code></strong></p><p>通过分值范围取出元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314080942.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314080942.png" srcset="" alt="image-20210314080941980"><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314080959.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314080959.png" srcset="" alt="image-20210314080959723"></p></blockquote><blockquote><p><strong><code>zrevrange</code></strong></p><p>逆向取出元素。从大到小取出。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314083926.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314083926.png" srcset="" alt="image-20210314083926241"><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314083639.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314083639.png" srcset="" alt="image-20210314083639171"></p></blockquote><blockquote><p><strong><code>zscore</code></strong></p><p>通过元素取出分值。<img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084020.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084020.png" srcset="" alt="image-20210314084020929"><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084238.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084238.png" srcset="" alt="image-20210314084238321"></p></blockquote><blockquote><p><strong><code>zrank</code></strong></p><p>通过元素，取出排名，就是索引角标。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084455.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084455.png" srcset="" alt="image-20210314084455313"></p></blockquote><blockquote><p><strong><code>zincrby</code></strong></p><p>增加元素的分值，并且根据分值实时维护，元素的顺序。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084753.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314084753.png" srcset="" alt="image-20210314084753185"></p><p><strong>🎗场景：</strong></p><p>歌曲排行榜。倒叙显示。每播放一次+1.</p></blockquote><blockquote><p><strong><code>zunionstore</code></strong></p><p>并集操作。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314100629.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314100629.png" srcset="" alt="image-20210314100615228"><br> 参数numkeys表示参数运算的key。<br> weight权重，与对应集合中的分值相乘计算后，参数与最后的集合与几何间的运算。</p><p>sum|min|max，集合元素的运算，默认为加法。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314101133.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314101133.png" srcset="" alt="image-20210314101133351"></p><p><strong>权重运算：</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314101847.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314101847.png" srcset="" alt="image-20210314101847231"></p><p><strong>求最大值运算：</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314102354.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210314102354.png" srcset="" alt="image-20210314102354089"></p></blockquote><h2 id="管道pipeline的使用"><a class="header-anchor" href="#管道pipeline的使用">☀</a>管道pipeline的使用</h2><blockquote><p>接住管道可以一次发送多个命令，节省往返时间。</p></blockquote><blockquote><p>建立socket链接<br> <code>nc localhost 6379</code></p><p>将多个命令，通过管道传入redis执行。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> -e <span class="string">&quot;set k2 99\nincr k2\n get k2 | nc localhost 6379&quot;</span></span><br></pre></td></tr></table></figure><p>一般用于冷加载，启动的时候，将一些预留的数据放到redis中。</p></blockquote><h2 id="发布订阅"><a class="header-anchor" href="#发布订阅">☀</a>发布订阅</h2><p><strong>pubsub</strong></p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/08/20210315202134.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/08/20210315202134.png" srcset="" alt="image-20210315202134107"></p><blockquote><p><strong><code>publish</code></strong></p><p>向管道中添加元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315202347.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315202347.png" srcset="" alt="image-20210315202347140"></p><p><strong><code>SUBSCRIBE</code></strong></p><p>接受元素,监听之后别人发的消息才能收到.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315202450.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315202450.png" srcset="" alt="image-20210315202449934"></p></blockquote><p>✌🤞**使用场景：**微信聊天窗口。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315204035.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315204035.png" srcset="" alt="image-20210315204035666"></p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315210755.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315210755.png" srcset="" alt="image-20210315210755333"></p><h2 id="事务的使用"><a class="header-anchor" href="#事务的使用">☀</a>事务的使用</h2><p>永远记住一句话，速度快时特征，才会去选用Redis。Redis设计的事务也是往速度方面设计考虑，不是特别复杂。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315214441.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315214441.png" srcset="" alt="image-20210315212104221"></p><blockquote><p>为什么 Redis 不支持回滚（roll back）<br> 如果你有使用关系式数据库的经验， 那么 “Redis 在事务失败时不进行回滚，而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。以下是这种做法的优点：</p><p>​ Redis 命令只会因为错误的语法而失败（并且这些问题不能在入队时发现），或是命令用在了错误类型的键上面：这也就是说，从实用性的角度来说，失败的命令是由编程错误造成的，而这些错误应该在开发的过程中被发现，而不应该出现在生产环境中。<br> 因为不需要对回滚进行支持，所以 Redis 的内部可以保持简单且快速。<br> ​ 有种观点认为 Redis 处理事务的做法会产生 bug ， 然而需要注意的是， 在通常情况下， 回滚并不能解决编程错误带来的问题。 举个例子， 如果你本来想通过 INCR 命令将键的值加上 1 ， 却不小心加上了 2 ， 又或者对错误类型的键执行了 INCR ， 回滚是没有办法处理这些情况的。</p></blockquote><blockquote><p><strong><code>MULTI</code></strong><br> 开启事务</p></blockquote><blockquote><p><strong><code>EXEC</code></strong><br> 关闭事务，谁的exe先到达，谁的事务就先执行。</p></blockquote><blockquote><p><strong><code>WATCH</code></strong><br> 乐观锁。</p></blockquote><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315212201.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315212201.png" srcset="" alt="image-20210315212201608"></p><blockquote><p>例：</p><p>窗口1：watch k1 开启事务<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315213811.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315213811.png" srcset="" alt="image-20210315213811132"><br> 窗口2：开启事务，修改k1,关闭执行事务。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315214119.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315214119.png" srcset="" alt="image-20210315214119091"></p><p>窗口3：执行事务，由于k1被修改了，事务执行失败。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315214010.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210315214010.png" srcset="" alt="image-20210315214010903"></p></blockquote><h2 id="Module布隆过滤器的使用"><a class="header-anchor" href="#Module布隆过滤器的使用">☀</a>Module布隆过滤器的使用</h2><p>增加Redis的功能。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210316204956.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/15/20210316204956.png" srcset="" alt="image-20210316204956533"></p><h3 id="安装bloom-filter"><a class="header-anchor" href="#安装bloom-filter">☀</a>安装bloom-filter</h3><blockquote><ol><li><p><a target="_blank" rel="noopener external nofollow noreferrer" href="http://xn--redis-6u8oj48c.io">访问redis.io</a></p></li><li><p>点开modules</p></li><li><p>访问RedisBloom的github<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/RedisBloom/RedisBloom">https://github.com/RedisBloom/RedisBloom</a></p></li><li><p>linux中wget *.zip</p></li><li><p>yum install unzip</p></li><li><p>unzip *.zip</p></li><li><p>make</p></li><li><p>cp <a target="_blank" rel="noopener external nofollow noreferrer" href="http://bloom.so">bloom.so</a> /opt/mashibing/redis5/</p></li><li><p>redis-server --loadmodule /usr/local/redis/redisbloom.so 。<br> 🎨**注意：**此处启动，关联布隆过滤器的函数库redisbloom.so文件一定要用绝对路径。不能指定配置文件。才能启动成功。是属于临时启动。</p></li></ol></blockquote><h3 id="命令"><a class="header-anchor" href="#命令">☀</a>命令</h3><blockquote><p><strong><code>BF.ADD</code></strong></p><p>布隆过滤器中添加元素。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327200702.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327200702.png" srcset="" alt="image-20210327200702582"><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201004.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201004.png" srcset="" alt="image-20210327201004521"></p></blockquote><blockquote><p><strong><code>BF.EXISTS</code></strong></p><p>验证是否存在。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327200945.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327200945.png" srcset="" alt="image-20210327200945858"></p><p>返回<code>1</code>存在，返回<code>0</code>不存在。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201024.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201024.png" srcset="" alt="image-20210327201024168"></p></blockquote><blockquote><p><strong><code>CF.ADD</code></strong></p><p>布谷鸟过滤器相关的命令。</p></blockquote><h3 id="解决穿透问题"><a class="header-anchor" href="#解决穿透问题">☀</a>解决穿透问题</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201127.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201127.png" srcset="" alt="image-20210327201127446"></p><blockquote><p>概率解决问题，不可能百分百阻挡，&gt;1%</p><p>1，你有啥</p><p>2，有的向bitmap中标记</p><p>3，请求的可能被误标记</p><p>4，但是，一定概率会大量减少放行：穿透</p><p>5，而且，成本低</p></blockquote><h3 id="几种分布式实现方式"><a class="header-anchor" href="#几种分布式实现方式">☀</a>几种分布式实现方式</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201246.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327201246.png" srcset="" alt="image-20210327201246738"><br> 第三种最优。最符合分布式架构的思想。</p><h3 id="过滤器种类介绍"><a class="header-anchor" href="#过滤器种类介绍">☀</a>过滤器种类介绍</h3><blockquote><p>过滤器：</p><p>bloom</p><p>布隆过滤器</p><p>counting bloom</p><p>cukcoo</p><p>布谷鸟过滤器</p></blockquote><h3 id="穿透了，但是数据库不存在的处理"><a class="header-anchor" href="#穿透了，但是数据库不存在的处理">☀</a>穿透了，但是数据库不存在的处理</h3><blockquote><ol><li><p>client，增加redis中的key，value标记.</p></li><li><p>数据库增加了元素.</p></li><li><p>完成元素对bloom的添加.</p></li></ol></blockquote><p>过滤器相当于是对数据库存量的一个小的映射。</p><h2 id="Redis作为缓存"><a class="header-anchor" href="#Redis作为缓存">☀</a>Redis作为缓存</h2><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210320103557.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/13/20210320103557.png" srcset="" alt="image-20210320103550760"></p><blockquote><p>设置key的存活时长</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">set</span> k1 ex 50 <span class="comment">#设置k1存活时长为50秒.</span></span><br></pre></td></tr></table></figure></blockquote><blockquote><p><strong><code>ttl key</code></strong><br> 查看key剩余存活时长.</p><table><thead><tr><th>结果</th><th>描述</th></tr></thead><tbody><tr><td><code>-2</code></td><td><strong>表示key已经过期，或者不存在。</strong></td></tr><tr><td><code>-1</code></td><td>表示key，一直存在。没有过期时间。</td></tr><tr><td><code>正数</code></td><td>表示key的剩余过期时间，单位秒。</td></tr></tbody></table></blockquote><blockquote><p><strong><code>expire</code></strong></p><p>设置key的过期时间。单位秒。几秒后过期。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327094223.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327094223.png" srcset="" alt="image-20210327094216499"></p><p><strong>🎭注意：发生写会剔除过期时间。只针存在的key或者未过期的key有效。对已经过期，或者不存在的key设置无效。</strong></p><p>🤗思考为什么会剔除过期时间？</p><p>设置新值的时候，新值存放在一个新的物理地址中，key指向新的value的物理地址，过期时间针对的是老的value。</p></blockquote><blockquote><p><strong><code>expireat</code></strong></p><p>设置key在将来的某个时间节点过期。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327094745.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/03/20/20210327094745.png" srcset="" alt="image-20210327094745511"></p></blockquote><blockquote><p><strong><code>time</code></strong></p><p>取时间戳.</p></blockquote><h3 id="回收策略"><a class="header-anchor" href="#回收策略">☀</a>回收策略</h3><blockquote><p>当maxmemory限制达到的时候Redis会使用的行为由 Redis的maxmemory-policy配置指令来进行配置。</p><p>以下的策略是可用的:</p><ul><li><strong>noeviction</strong>:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令（大部分的写入指令，但DEL和几个例外）</li><li><strong>allkeys-lru</strong>: 尝试回收最少使用的键（LRU），使得新添加的数据有空间存放。</li><li><strong>volatile-lru</strong>: 尝试回收最少使用的键（LRU），但仅限于在过期集合的键,使得新添加的数据有空间存放。</li><li><strong>allkeys-random</strong>: 回收随机的键使得新添加的数据有空间存放。</li><li><strong>volatile-random</strong>: 回收随机的键使得新添加的数据有空间存放，但仅限于在过期集合的键。</li><li><strong>volatile-ttl</strong>: 回收在过期集合的键，并且优先回收存活时间（TTL）较短的键,使得新添加的数据有空间存放。</li></ul></blockquote><h3 id="过期淘汰原理"><a class="header-anchor" href="#过期淘汰原理">☀</a>过期淘汰原理:</h3><blockquote><p>Redis keys过期有两种方式：被动和主动方式。</p><p>当一些客户端尝试访问它时，key会被发现并主动的过期。</p><p>当然，这样是不够的，因为有些过期的keys，永远不会访问他们。 无论如何，这些keys应该过期，所以定时随机测试设置keys的过期时间。所有这些过期的keys将会从密钥空间删除。</p><p>具体就是Redis每秒10次做的事情：</p><ol><li>测试随机的20个keys进行相关过期检测。</li><li>删除所有已经过期的keys。</li><li>如果有多于25%的keys过期，重复步奏1.</li></ol><p>这是一个平凡的概率算法，基本上的假设是，我们的样本是这个密钥控件，并且我们不断重复过期检测，直到过期的keys的百分百低于25%,这意味着，在任何给定的时刻，最多会清除1/4的过期keys。</p></blockquote><h2 id="持久化"><a class="header-anchor" href="#持久化">☀</a>持久化</h2><p>一个进程既要满足修改，又要满足持久化，成本很高。</p><p>Redis的存储层：</p><ol><li>快照/副本</li><li>日志</li></ol><h3 id="RDB"><a class="header-anchor" href="#RDB">☀</a>RDB</h3><h4 id="RDB持久化可实现的几种方式"><a class="header-anchor" href="#RDB持久化可实现的几种方式">☀</a>RDB持久化可实现的几种方式:</h4><h5 id="阻塞，数据拷贝"><a class="header-anchor" href="#阻塞，数据拷贝">☀</a>阻塞，数据拷贝:</h5><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408111750225.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408111750225.png" srcset="" alt="image-20210408111750225"></p><blockquote><p>save命令实现</p></blockquote><p>弊端:不能对外提供服务.明确关机维护的时候可以使用此命令。</p><h5 id="非阻塞，数据拷贝"><a class="header-anchor" href="#非阻塞，数据拷贝">☀</a>非阻塞，数据拷贝:</h5><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408111917373.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408111917373.png" srcset="" alt="image-20210408111917373"></p><p>弊端:时点混乱.不使用此方式。</p><h5 id="非阻塞-fork"><a class="header-anchor" href="#非阻塞-fork">☀</a>非阻塞,fork</h5><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408112309792.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408112309792.png" srcset="" alt="image-20210408112309792"></p><blockquote><p>1.bgsave命令实现。</p><p>2.配置文件中给出bgsave的规则。</p></blockquote><p>fork原理图：</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408173910856.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408173910856.png" srcset="" alt="image-20210408173910856"></p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408112513691.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408112513691.png" srcset="" alt="image-20210408112513691"></p><h4 id="优缺点："><a class="header-anchor" href="#优缺点：">☀</a>优缺点：</h4><h5 id="😜优点："><a class="header-anchor" href="#😜优点：">☀</a>😜优点：</h5><ol><li><p>时点性</p></li><li><p>类似java中的序列化 恢复的速度相对快</p></li></ol><h5 id="🤔缺点："><a class="header-anchor" href="#🤔缺点：">☀</a>🤔缺点：</h5><ol><li><p>不支持拉链 只有一个dump.rdb</p></li><li><p>丢失数据相对多一些 时点与时点之间窗口数据容易丢失。</p><p>8得到一个rdb，9纲要洛一个rdb，挂机了</p></li></ol><h4 id="fork介绍"><a class="header-anchor" href="#fork介绍">☀</a>fork介绍</h4><h5 id="进程的概念"><a class="header-anchor" href="#进程的概念">☀</a>进程的概念</h5><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408220231613.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408220231613.png" srcset="" alt="image-20210408220231613"></p><blockquote><p>父子进程之间的关系：</p><ol><li><p>进程之间是数据隔离的。</p></li><li><p>父进程其实可以让子进程看到数据，当刚创建还未修改时，本质是子进程中的指针与父进程中的一致。</p></li><li><p>子进程修改变量，不会影响父进程。父进程中修改变量也不会影响子进程。</p></li></ol></blockquote><blockquote><p>创建子进程需要考虑的维度：</p><ol><li><p>速度。</p></li><li><p>内存空间够不够。</p></li></ol></blockquote><blockquote><p>fork()方法特点：</p><ol><li>速度快。</li><li>占用内存小。</li></ol></blockquote><p>fork过程图解：</p><p>copy on write机制。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408221102660.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210408221102660.png" srcset="" alt="image-20210408221102660"></p><p>💖重点一句话：玩的都是指针。</p><h4 id="RDB配置："><a class="header-anchor" href="#RDB配置：">☀</a>RDB配置：</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">save 900 1			<span class="comment">#900秒内超过1条数据。</span></span><br><span class="line">save 300 10		  	<span class="comment">#300秒内超过10秒数据。</span></span><br><span class="line">save 60 10000	  	<span class="comment">#60秒内超过10000条数据。</span></span><br><span class="line">dbfilename dump.rdb		<span class="comment">#存储的文件名。</span></span><br><span class="line"><span class="built_in">dir</span> /var/lib/redis/6379	<span class="comment">#存储的文件目录。</span></span><br></pre></td></tr></table></figure><h3 id="AOF"><a class="header-anchor" href="#AOF">☀</a>AOF</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409090933347.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409090933347.png" srcset="" alt="image-20210409090933347"></p><h4 id="AOF概念："><a class="header-anchor" href="#AOF概念：">☀</a>AOF概念：</h4><blockquote><p>redis的写操作记录到文件中.</p></blockquote><h4 id="优点："><a class="header-anchor" href="#优点：">☀</a>优点：</h4><blockquote><p>丢失的数据量少。</p></blockquote><h4 id="AOF发展史"><a class="header-anchor" href="#AOF发展史">☀</a>AOF发展史</h4><h5 id="4-0以前"><a class="header-anchor" href="#4-0以前">☀</a>4.0以前</h5><blockquote><p>弊端：体量无限变大，恢复慢。</p><p>RDB和AOF可以同时开启，如果开启了AOF 只会用AOF恢复。</p><p>日志方案：hdfs，fsimage+edits.log 让日志只记录增量 合并的过程。</p><p>重写策略–删除抵消的命令 合并重复的命令。最终也是一个 纯指令的日志文件。</p></blockquote><h5 id="4-0以后"><a class="header-anchor" href="#4-0以后">☀</a>4.0以后</h5><blockquote><p>将老的数据RDB到aof文件中 将增量的以指令的方式 Append到AOF。</p><p>AOF是一个混合体 利用了RDB的快 利用了日志的全量。</p><p>重写命令执行之后，才会变成混合体。</p><p>增量日志+全量时点数据。恢复速度快，数据更全面。</p></blockquote><h4 id="AOF配置："><a class="header-anchor" href="#AOF配置：">☀</a>AOF配置：</h4><p>AOF记录时，写操作会触发IO。根据数据重要性，选择合适的写操作。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">appendonly <span class="built_in">yes</span>						<span class="comment">#开启aof</span></span><br><span class="line">appendfilename <span class="string">&quot;appendonly.aof&quot;</span>		<span class="comment">#文件名</span></span><br><span class="line">auto-aof-rewrite-percentage 100		<span class="comment">#判断文件达到多大百分比时重写。</span></span><br><span class="line">auto-aof-rewrite-min-size 64mb		<span class="comment">#自动重写，初始的文件容量。</span></span><br><span class="line">appendfsync always					<span class="comment">#刷新buffer内存。</span></span><br><span class="line">appendfsync everysec				<span class="comment">#每秒刷新buffer内存</span></span><br><span class="line">appendfsync no						<span class="comment">#只有当buffer满时刷新内存。</span></span><br></pre></td></tr></table></figure><p>往磁盘写操作内部理解：</p><p>只要是写操作，都会调用系统内核，内核中间有个buffer，缓冲区。就是用来减少交互次数。提升效率。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409092646328.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409092646328.png" srcset="" alt="image-20210409092646328"></p><h3 id="实操"><a class="header-anchor" href="#实操">☀</a>实操</h3><p><strong>🙄配置文件相关：</strong></p><p>设置redis，后台跑，还是前台阻塞跑。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">daemonize  <span class="built_in">yes</span>/no</span><br></pre></td></tr></table></figure><p>设置输出日志文件路径：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">logfile /var/log/redis_6379.<span class="built_in">log</span></span><br></pre></td></tr></table></figure><p>如果关闭了后台运行，日志也可以关闭，使其在控制台前台打印。</p><p>开启Aof追加模式</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">appendonly <span class="built_in">yes</span></span><br></pre></td></tr></table></figure><p>aof-rdb混合开关</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aof-use-rdb-preamble no/yes</span><br></pre></td></tr></table></figure><p><strong>😏命令相关：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">save  <span class="comment">#阻塞方式rdb保存数据</span></span><br><span class="line">bgsave <span class="comment">#后台非阻塞方式保存rdb数据  fork()，保存为rdb文件。</span></span><br><span class="line">bgrewirteaof  <span class="comment">#后台重写aof文件，命令整合。4，0之后为rdb+aof混合体。保存为aof文件。</span></span><br></pre></td></tr></table></figure><h2 id="AFK原理"><a class="header-anchor" href="#AFK原理">☀</a>AFK原理</h2><h3 id="单机存在的问题"><a class="header-anchor" href="#单机存在的问题">☀</a>单机存在的问题</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409164905534.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409164905534.png" srcset="" alt="image-20210409164905534"></p><blockquote><ol><li>单点故障。</li><li>容量有限。</li><li>压力。</li></ol></blockquote><h3 id="AFK策略图解image-20210409165209783"><a class="header-anchor" href="#AFK策略图解image-20210409165209783">☀</a>AFK策略图解<img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409165209783.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409165209783.png" srcset="" alt="image-20210409165209783"></h3><blockquote><p>AKF</p><p>X轴：每一台都是全量，有一台为主，其他为镜像。</p><p>Y轴：通过业务和功能区分。分段。</p><p>Z轴：每种业务，优先级，逻辑再拆分。将数据落到不同的片上。分片。</p></blockquote><h3 id="AFK一变多，数据一致性解决方案，针对x轴。"><a class="header-anchor" href="#AFK一变多，数据一致性解决方案，针对x轴。">☀</a>AFK一变多，数据一致性解决方案，针对x轴。</h3><blockquote><p>通过AKF一变多</p><p>需要解决的问题：数据一致性！！！！</p><p>反问自己：为什么一变多，解决可用性。</p></blockquote><p><strong>同步阻塞法</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409170034047.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409170034047.png" srcset="" alt="image-20210409170034047"></p><blockquote><p>强一致性。但是，如果阻塞时间过长，破坏可用性。</p></blockquote><p><strong>异步法</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409170312690.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409170312690.png" srcset="" alt="image-20210409170312690"></p><blockquote><p>弱一致性，如果异步处理出现失败异常，会丢失数据。</p></blockquote><p><strong>kafka法</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409170554416.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/image-20210409170554416.png" srcset="" alt="image-20210409170554416"></p><blockquote><p>redis与kafka之间为同步阻塞。redis和kafka之间为异步。由于kafka的可靠性，一定会同步到其他redis中。</p><p>期间可能会取到不一致的数据。但是最终数据会一直。强调：强一致性。</p></blockquote><h3 id="主备、主从、主主概念区分："><a class="header-anchor" href="#主备、主从、主主概念区分：">☀</a>主备、主从、主主概念区分：</h3><p>参考博客链接：<a target="_blank" rel="noopener external nofollow noreferrer" href="https://www.cnblogs.com/tankblog/p/11190598.html">https://www.cnblogs.com/tankblog/p/11190598.html</a></p><blockquote><p>主备 主从 主主模式<br> 单点故障的情况不可避免，而且单副本的存储方案早已无法满足业务的可靠性要求，单机可靠性就就两个9，也就是一年大概有3.65天不可用。因此一般情况下我们至少也会上个双机存储架构。凡事最好有个plan B。</p></blockquote><h4 id="主备"><a class="header-anchor" href="#主备">☀</a>主备</h4><p>主：主机，备：备机。<br> 主机的意思当然是以它为主了，读写都是主机上，而备机呢就是备用，默默的在背后吸收主机的数据，时刻待命着等待主机挂了之后取而代之(没这么坏哈哈)。因此在主机还活着的情况下，备机的唯一使命就是同步主机的数据，不对外提供服务。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/16034279-6ee5bd51875b6fb6" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/16034279-6ee5bd51875b6fb6" srcset="" alt="img"></p><p>image<br> <strong>❀优点</strong>：简单，主备之间只有数据同步，不需要考虑别的情况。就很简单的配置一下，再搞一台服务器就能组成主备架构了。</p><p><strong>❀缺点</strong>：备机等于就拿来备份，浪费了备机这台服务器的资源。上面说的不考虑别的情况指的是主机和备机它们两之间就只要复制数据.</p><p>但是有些情况我们人还是得考虑的：主机挂了如何让备机上。有三种选择:</p><ol><li><p>人工切换。人工切换时效性不高，出了事情首先你得开机，登录远程一阵啪啪得好几分钟或者万一你在LOL，黑铁晋级青铜最后一把努力了几个月即将晋升倔强青铜的一刻！是吧。还要万一在深夜或者说…是吧。</p></li><li><p>引入中间件。例如ZooKeeper、keepalived。就跟好多房东把房子委托给中介一样，这中间件就是个中介。全权由中介来打理主机和备机，它会根据机子状态来判别这时候是不是该备机上了。(建议)</p></li><li><p>主机备机之间状态传输(咱不找中介了，自己来打理)，啥意思呢？就是除数据同步，主备之间还要有个状态传输过程，来让备机只要现在主机过得好不好，可以是主机主动推送它的状态给备机，或者是备机去索要状态。当状态拿不到或者不对的时候就开始主备切换。但是可能传输出现了波动啥的，导致备机误判了，然后备机升级为主机，这样就两主机了(下面会说主主的问题)。</p></li></ol><h4 id="主从"><a class="header-anchor" href="#主从">☀</a>主从</h4><p>主：主机，从：从机</p><p>从机和备机的区别在于它得除了同步数据之外还得干活，对外提供读的操作，你可以理解为它是仆从。但是仆从和备机一样也有翻身做主人的一天，所以它也在默默的等待着主机挂了，取而代之。image<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/16034279-f5c770b075861e55" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/16034279-f5c770b075861e55" srcset="" alt=""></p><p><strong>❀优点：</strong><br> 充分利用了资源，嘿嘿不想备机这么爽了，还得出来干活，对外提供读操作。而且在主机挂了的时候，如果没任命新机主之前，读操作还是能用的。</p><p><strong>❀缺点：</strong></p><ol><li><p>客户端需要多个判断，也就是不同操作需要发放给不同服务器，我上图主机提供读写，有时候读写分离了，主机就提供写。</p></li><li><p>主从延迟，读操作分配给从库，就会存在数据同步的延迟问题，比如某个人注册了账号之后，登录走的是从机，这时候数据还未从主机同步过来，那可不让人很难受了。有关主从延迟问题的一些解决办法</p></li><li><p>和主备一样的切换问题。(参考主备)</p></li></ol><h4 id="主主"><a class="header-anchor" href="#主主">☀</a>主主</h4><p>主主就是两台都是主机。同时对外提供读写操作。客户端任意访问提供的一台。<br> image<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/16034279-87106a84dfe9c321" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/08/16034279-87106a84dfe9c321" srcset="" alt="img"></p><p><strong>❀优点：</strong></p><p>​ 主主的好处就是可以把写操作也分担一下，但是问题恰恰就出在写操作上，导致主主的架构有很大的局限性。</p><p><strong>❀缺点：</strong></p><p>​ 例如主机A有个注册的插入操作，生成的id是50，同一时刻主机B也有个插入操作生成的id也是50。然后它们之间的数据同步了，你说是谁覆盖谁呢？谁覆盖谁都不对！</p><p>​ 因此主主只适用于可以双向复制，覆盖的数据(例如用户登录生成的token)。但是我们平日里绝大部分的数据都不允许。</p><p><strong>结语</strong><br> 这种双机存储架构一般而言应用于一些业务量不大的场景。主要还是为了存储的可用性。</p><h2 id="CAP"><a class="header-anchor" href="#CAP">☀</a>CAP</h2><h3 id="CAP概念"><a class="header-anchor" href="#CAP概念">☀</a>CAP概念</h3><p>参考链接：<a target="_blank" rel="noopener external nofollow noreferrer" href="http://www.ruanyifeng.com/blog/2018/07/cap.html">http://www.ruanyifeng.com/blog/2018/07/cap.html</a></p><p>分布式系统（distributed system）正变得越来越重要，大型网站几乎都是分布式的。</p><p>分布式系统的最大难点，就是各个节点的状态如何同步。CAP 定理是这方面的基本定理，也是理解分布式系统的起点。</p><p>本文介绍该定理。它其实很好懂，而且是显而易见的。下面的内容主要参考了 Michael Whittaker 的<a target="_blank" rel="noopener external nofollow noreferrer" href="https://mwhittaker.github.io/blog/an_illustrated_proof_of_the_cap_theorem/">文章</a>。</p><h4 id="一、分布式系统的三个指标"><a class="header-anchor" href="#一、分布式系统的三个指标">☀</a>一、分布式系统的三个指标</h4><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/09/bg2018071607.jpg" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/09/bg2018071607.jpg" srcset="" alt="img"></p><p>1998年，加州大学的计算机科学家 Eric Brewer 提出，分布式系统有三个指标。</p><blockquote><ul><li>Consistency</li><li>Availability</li><li>Partition tolerance</li></ul></blockquote><p>它们的第一个字母分别是 C、A、P。</p><p>Eric Brewer 说，这三个指标不可能同时做到。这个结论就叫做 CAP 定理。</p><h4 id="二、Partition-tolerance"><a class="header-anchor" href="#二、Partition-tolerance">☀</a>二、Partition tolerance</h4><p>先看 Partition tolerance，中文叫做&quot;分区容错&quot;。</p><p>大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区（partition）。分区容错的意思是，区间通信可能失败。比如，一台服务器放在中国，另一台服务器放在美国，这就是两个区，它们之间可能无法通信。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418132938.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418132938.png" srcset="" alt="img"></p><p>上图中，G1 和 G2 是两台跨区的服务器。G1 向 G2 发送一条消息，G2 可能无法收到。系统设计的时候，必须考虑到这种情况。</p><p>一般来说，分区容错无法避免，因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们，剩下的 C 和 A 无法同时做到。</p><h4 id="三、Consistency"><a class="header-anchor" href="#三、Consistency">☀</a>三、Consistency</h4><p>Consistency 中文叫做&quot;一致性&quot;。意思是，写操作之后的读操作，必须返回该值。举例来说，某条记录是 v0，用户向 G1 发起一个写操作，将其改为 v1。</p><p><img src="https://www.wangbase.com/blogimg/asset/201807/bg2018071602.png" class="lazyload" data-srcset="https://www.wangbase.com/blogimg/asset/201807/bg2018071602.png" srcset="" alt="img"></p><p>接下来，用户的读操作就会得到 v1。这就叫一致性。</p><p><img src="https://www.wangbase.com/blogimg/asset/201807/bg2018071603.png" class="lazyload" data-srcset="https://www.wangbase.com/blogimg/asset/201807/bg2018071603.png" srcset="" alt="img"></p><p>问题是，用户有可能向 G2 发起读操作，由于 G2 的值没有发生变化，因此返回的是 v0。G1 和 G2 读操作的结果不一致，这就不满足一致性了。</p><p><img src="https://www.wangbase.com/blogimg/asset/201807/bg2018071604.png" class="lazyload" data-srcset="https://www.wangbase.com/blogimg/asset/201807/bg2018071604.png" srcset="" alt="img"></p><p>为了让 G2 也能变为 v1，就要在 G1 写操作的时候，让 G1 向 G2 发送一条消息，要求 G2 也改成 v1。</p><p><img src="https://www.wangbase.com/blogimg/asset/201807/bg2018071605.png" class="lazyload" data-srcset="https://www.wangbase.com/blogimg/asset/201807/bg2018071605.png" srcset="" alt="img"></p><p>这样的话，用户向 G2 发起读操作，也能得到 v1。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418132952.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418132952.png" srcset="" alt="img"></p><h4 id="四、Availability"><a class="header-anchor" href="#四、Availability">☀</a>四、Availability</h4><p>Availability 中文叫做&quot;可用性&quot;，意思是只要收到用户的请求，服务器就必须给出回应。</p><p>用户可以选择向 G1 或 G2 发起读操作。不管是哪台服务器，只要收到请求，就必须告诉用户，到底是 v0 还是 v1，否则就不满足可用性。</p><h4 id="五、Consistency-和-Availability-的矛盾"><a class="header-anchor" href="#五、Consistency-和-Availability-的矛盾">☀</a>五、Consistency 和 Availability 的矛盾</h4><p>一致性和可用性，为什么不可能同时成立？答案很简单，因为可能通信失败（即出现分区容错）。</p><p>如果保证 G2 的一致性，那么 G1 必须在写操作时，锁定 G2 的读操作和写操作。只有数据同步后，才能重新开放读写。锁定期间，G2 不能读写，没有可用性不。</p><p>如果保证 G2 的可用性，那么势必不能锁定 G2，所以一致性不成立。</p><p>综上所述，G2 无法同时做到一致性和可用性。系统设计时只能选择一个目标。如果追求一致性，那么无法保证所有节点的可用性；如果追求所有节点的可用性，那就没法做到一致性。</p><p>在什么场合，可用性高于一致性？</p><p>举例来说，发布一张网页到 CDN，多个服务器有这张网页的副本。后来发现一个错误，需要更新网页，这时只能每个服务器都更新一遍。</p><p>一般来说，网页的更新不是特别强调一致性。短时期内，一些用户拿到老版本，另一些用户拿到新版本，问题不会特别大。当然，所有人最终都会看到新版本。所以，这个场合就是可用性高于一致性。</p><p>实时证明，大多数都是牺牲了一致性。像12306还有淘宝网，就好比是你买火车票，本来你看到的是还有一张票，其实在这个时刻已经被买走了，你填好了信息准备买的时候发现系统提示你没票了。这就是牺牲了一致性。</p><p>但是不是说牺牲一致性一定是最好的。就好比mysql中的事务机制，张三给李四转了100块钱，这时候必须保证张三的账户上少了100，李四的账户多了100。因此需要数据的一致性，而且什么时候转钱都可以，也需要可用性。但是可以转钱失败是可以允许的，这时候舍弃可用性。</p><h3 id="Redis中主从CAP监控。"><a class="header-anchor" href="#Redis中主从CAP监控。">☀</a>Redis中主从CAP监控。</h3><blockquote><p>Redis中的集群关系，采用主从</p></blockquote><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418143351.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418143351.png" srcset="" alt="image-20210418143351732"></p><blockquote><p>监控法，实行主备切换，由多个监控程序集群监控主。</p></blockquote><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418143510.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418143510.png" srcset="" alt="image-20210418143510871"></p><blockquote><p>监控集群，势力范围n/2+1,一般使用奇数台。</p><p>奇数和偶数允许风险的个数一样，但是发生风险的个数偶数大于奇数，所以选择奇数台。</p></blockquote><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418143933.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418143933.png" srcset="" alt="image-20210418143933529"></p><h2 id="Redis主从复制"><a class="header-anchor" href="#Redis主从复制">☀</a>Redis主从复制</h2><blockquote><p>使用经验：</p><ol><li>从机如果是RDB模式，无论从机挂后重启或者从机运行时，每次同步数据，可从RDB文件中记录的ID给出偏移量，给到主，最后同步少量的增量。如果时AOF模式，每次同步数据都是全量同步。</li></ol><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418204223.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418204223.png" srcset="" alt="image-20210418204223363"></p><ol start="2"><li>同步数据时从节点会先删除老数据，再同步新数据。</li></ol></blockquote><h3 id="开启主从复制："><a class="header-anchor" href="#开启主从复制：">☀</a>开启主从复制：</h3><h4 id="使用命令的方式："><a class="header-anchor" href="#使用命令的方式：">☀</a>使用命令的方式：</h4><p><strong>❀redis运行时</strong></p><p><code>SLAVEOF</code></p><p><code>REPLICAOF host port</code> redis5版本后可以使用。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418203435.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418203435.png" srcset="" alt="image-20210418203435087"></p><p><strong>❀redis启动时：</strong></p><p>不仅设置了主，还设置了AOF持久化的方式。</p><p><code>redis-server ./6380.conf --replicaof 127.0.0.1 6379 --appendonly yes</code></p><p>个人理解<code>--参数</code>相当于覆盖原有的配置文件中的参数。</p><h4 id="通过配置文件方式："><a class="header-anchor" href="#通过配置文件方式：">☀</a>通过配置文件方式：</h4><p>设置链接主redis的ip和端口：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">replicaof &lt;masterip&gt; &lt;masterport&gt;</span><br></pre></td></tr></table></figure><p>设置链接主redis的参数：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">masterauth &lt;master-password&gt;</span><br></pre></td></tr></table></figure><p>设置下载主最新rdb时，老数据是否可读。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">replica-serve-stale-data yes</span><br></pre></td></tr></table></figure><p>设置从只读</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">replica-read-only yes</span><br></pre></td></tr></table></figure><p>设置同步方式，间接通过磁盘还是，直接网络IO。如果磁盘速度很慢，网络带宽很快就直接通过网络IO。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repl-diskless-sync no</span><br></pre></td></tr></table></figure><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418205356.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418205356.png" srcset="" alt="image-20210418205356737"></p><p>设置在同步增量数据时的存放队列的最大容量。如果超出最大容量，则直接使用全量RDB同步。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repl-backlog-size 1mb</span><br></pre></td></tr></table></figure><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418205707.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210418205707.png" srcset="" alt="image-20210418205707233"></p><p>#增量复制,至少写成功三条。主从异步同步数据的弱一致性，往强一致性上靠。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">min-replicas-to-write 3</span><br><span class="line">min-replicas-max-lag 10</span><br></pre></td></tr></table></figure><h3 id="手工切换主备"><a class="header-anchor" href="#手工切换主备">☀</a>手工切换主备</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">`REPLICAOF NO ONE`  <span class="comment">#设置替换的从机不再追随。</span></span><br><span class="line">`REPLICAOF host port` <span class="comment">#在其他从机设置，重新追随指定的替换从机。</span></span><br></pre></td></tr></table></figure><h3 id="监控哨兵Sentinel"><a class="header-anchor" href="#监控哨兵Sentinel">☀</a>监控哨兵Sentinel</h3><blockquote><p>Redis 的 Sentinel 系统用于管理多个 Redis 服务器（instance）， 该系统执行以下三个任务：</p><ul><li><p><strong>监控（Monitoring</strong>）： Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。</p></li><li><p><strong>提醒（Notification）</strong>： 当被监控的某个 Redis 服务器出现问题时， Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。</p></li><li><p><strong>自动故障迁移（Automatic failover）</strong>： 当一个主服务器不能正常工作时， Sentinel 会开始一次自动故障迁移操作， 它会将失效主服务器的其中一个从服务器升级为新的主服务器， 并让失效主服务器的其他从服务器改为复制新的主服务器； 当客户端试图连接失效的主服务器时， 集群也会向客户端返回新主服务器的地址， 使得集群可以使用新主服务器代替失效服务器。</p><p>Redis Sentinel 是一个分布式系统， 你可以在一个架构中运行多个 Sentinel 进程（progress）， 这些进程使用流言协议（gossip protocols)来接收关于主服务器是否下线的信息， 并使用投票协议（agreement protocols）来决定是否执行自动故障迁移， 以及选择哪个从服务器作为新的主服务器。</p><p>虽然 Redis Sentinel 释出为一个单独的可执行文件 redis-sentinel ， 但实际上它只是一个运行在特殊模式下的 Redis 服务器， 你可以在启动一个普通 Redis 服务器时通过给定 –sentinel 选项来启动 Redis Sentinel 。</p></li></ul></blockquote><h4 id="启动哨兵"><a class="header-anchor" href="#启动哨兵">☀</a>启动哨兵</h4><blockquote><p>哨兵可以独立成一个进程，一个程序。也可以使redis服务进程包含哨兵这段代码。</p></blockquote><p>独立进场方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-sentienl  &lt;配置文件&gt;</span><br></pre></td></tr></table></figure><p>redis服务方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-server &lt;配置文件&gt;  --sentinel</span><br></pre></td></tr></table></figure><p>配置文件参考目录：下载的源码目录下。/usr/local/redis/redis-5.0.12</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">port 26379	<span class="comment">#配置哨兵端口号</span></span><br><span class="line">sentinel monitor mymaster 127.0.0.1 6379 2		<span class="comment">#配置监听主机的ip和端口，投票的权重数。</span></span><br></pre></td></tr></table></figure><h2 id="Spring整合Redis"><a class="header-anchor" href="#Spring整合Redis">☀</a>Spring整合Redis</h2><p>官网学习:<a target="_blank" rel="noopener external nofollow noreferrer" href="https://spring.io/projects/spring-data-redis">https://spring.io/projects/spring-data-redis</a></p><h2 id="Redis的Sharing分片逻辑"><a class="header-anchor" href="#Redis的Sharing分片逻辑">☀</a>Redis的Sharing分片逻辑</h2><blockquote><p>akf主要解决单机问题:</p><ol><li>单点故障.</li><li>容量问题.</li><li>访问压力.</li></ol><p>x,y,z轴思想.</p><p>主从复制，HA :从X轴,生成数据镜像,解决单点故障.</p><p>单节点：容量的问题,采取Sharing分片.</p></blockquote><h3 id="Y轴拆分方式"><a class="header-anchor" href="#Y轴拆分方式">☀</a>Y轴拆分方式</h3><p><strong>y轴按逻辑： 业务拆分</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419161302.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419161302.png" srcset="" alt="image-20210419161255353"></p><h3 id="Z轴Sharding分片逻辑"><a class="header-anchor" href="#Z轴Sharding分片逻辑">☀</a>Z轴Sharding分片逻辑:</h3><ol><li><strong>按算法：hash+取模modula</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419161445.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419161445.png" srcset="" alt="image-20210419161445349"></li></ol><blockquote><p>缺点:取模的数必须固定 %3 %4 %10,影响分布式下的扩展性,每次添加新节点必须全局洗牌.</p></blockquote><ol start="2"><li><strong>逻辑：random 随机</strong></li></ol><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419162253.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419162253.png" srcset="" alt="image-20210419162253337"></p><blockquote><p>random算法,用于存放list数据.例如实现消息队列场景.</p></blockquote><ol start="3"><li><p><strong>逻辑：kemata 一致性哈希,没有取模data，node</strong></p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419162746.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419162746.png" srcset="" alt="image-20210419162745964"></p><blockquote><p>将node参数一致性hash运算,挂在一个环形的hash环上(实际是挂在树结构的节点上).每次有新的key进来,参数一致性hash运算,按照大小找到最近的node,存入机器.</p></blockquote><blockquote><p>将node物理节点的实际ip计算,换成ip+数字,造出多个代表实际主机的虚拟节点,挂在哈希环上,解决数据倾斜问题.</p></blockquote><blockquote><p><strong>❀优点：</strong></p><p>你加节点，的确可以分担其他节点的压力，不会造成全局洗牌</p><p><strong>❀缺点：</strong></p><p>新增节点造成一小部分数据不能命中</p><p>1，问题，击穿，压到mysql</p><p>2，方案：没去取离我最近的2个物理节点</p><p>更倾向于 作为缓存，而不是数据库用！！！！</p></blockquote></li></ol><h2 id="Redis代理原理"><a class="header-anchor" href="#Redis代理原理">☀</a>Redis代理原理</h2><blockquote><p>代理可以解耦后面的Redis复杂度.</p></blockquote><h3 id="客户端直连Redis场景"><a class="header-anchor" href="#客户端直连Redis场景">☀</a>客户端直连Redis场景:</h3><p>redis 的连接成本很高 对server端造成的,客户端直连,在很多客户端的情况下,会造成连接数过多,增加了链接成本.三次握手消耗资源.</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419174749.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419174749.png" srcset="" alt="image-20210419174749585"></p><h3 id="使用Proxy代理链接Redis场景"><a class="header-anchor" href="#使用Proxy代理链接Redis场景">☀</a>使用Proxy代理链接Redis场景:</h3><p>达到负载均衡,减少链接的压力.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419182857.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419182857.png" srcset="" alt="image-20210419182857854"></p><blockquote><p>单台代理主机,可能访问量大的时候,proxy主机压力过大.</p></blockquote><h3 id="使用虚拟服务LVS-和keepalived监控-负载均衡代理Proxy"><a class="header-anchor" href="#使用虚拟服务LVS-和keepalived监控-负载均衡代理Proxy">☀</a>使用虚拟服务LVS,和keepalived监控,负载均衡代理Proxy</h3><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419183519.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419183519.png" srcset="" alt="image-20210419183519670"></p><blockquote><p>LVS,虚拟出一个VIP地址.</p><p>keepalived,监控LVS,使LVS高可用.监控proxy代理.</p></blockquote><h3 id="代理层的实现"><a class="header-anchor" href="#代理层的实现">☀</a>代理层的实现</h3><p>Redis分片逻辑在代理层实现:<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419184200.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/18/20210419184200.png" srcset="" alt="image-20210419184200061"></p><p>代理技术:</p><ol><li><p>twemproxy</p><p>github地址:<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/twitter/twemproxy">https://github.com/twitter/twemproxy</a></p></li><li><p>predixy</p></li><li><p>cluster(Redis自身)</p></li><li><p>codis</p></li></ol><h2 id="Redis中的cluster分片技术"><a class="header-anchor" href="#Redis中的cluster分片技术">☀</a>Redis中的cluster分片技术</h2><h3 id="预分区技术"><a class="header-anchor" href="#预分区技术">☀</a>预分区技术</h3><p>由于三种分片算法,modula,random,ketama,一旦集群增加主机后,都会出现找不到原有数据情况.所以只能用作缓存.不能用作数据库.</p><blockquote><p>实际分区两个,取模运算时,使用预分区个数10计算,算出的结果,在映射mapping下,转到实际主机.</p></blockquote><blockquote><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/19/20210419193006.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/19/20210419193006.png" srcset="" alt="image-20210419193006375">w</p><p>当集群中增加redis时,将mapping映射关系和对应数据实际分区同步转移到,新增的主机中.</p></blockquote><blockquote><p>redis自身集群中.每台都会带有一个hash算法,和映射mapping,客户端随机访问主机,都会跳转到正确的redis机器上.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/19/20210419193547.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/19/20210419193547.png" srcset="" alt="image-20210419193547162"></p></blockquote><blockquote><p>数据分治聚合操作很难实现事务</p></blockquote><h2 id="Redis集群搭建"><a class="header-anchor" href="#Redis集群搭建">☀</a>Redis集群搭建</h2><h3 id="twemproxy"><a class="header-anchor" href="#twemproxy">☀</a>twemproxy</h3><p>github地址：<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/twitter/twemproxy">https://github.com/twitter/twemproxy</a></p><p>简单安装搭建步骤：</p><ol><li><p>下载编译</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> git@github.com:twitter/twemproxy.git</span><br><span class="line"><span class="built_in">cd</span> twemproxy</span><br><span class="line">autoreconf -fvi</span><br><span class="line">./configure --enable-debug=full</span><br><span class="line">make</span><br><span class="line">src/nutcracker -h</span><br></pre></td></tr></table></figure></li><li><p>安装为服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cp</span> nutcracker.init /etc/init.d/twemproxy    <span class="comment">#将源码路径script文件夹下的nutcracker.init,放入服务脚本文件夹中.nutcracker.init内容可研究参考.</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> /etc</span><br><span class="line"></span><br><span class="line"><span class="built_in">mkdir</span> nutcracker  <span class="comment">#创建脚本指定的启动配置文件夹.</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cp</span> /usr/local/twemproxy/conf/*    /etc/nutcracker/   <span class="comment">#复制源码目录中的启动配置文件,进脚本指定的启动配置文件夹中.</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cp</span> nutcracker /usr/bin/   <span class="comment">#拷贝源码src目录下的nutcracker脚本到环境变量的目录中.</span></span><br><span class="line"></span><br><span class="line">service twemproxy start		<span class="comment">#启用代理服务.</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></li></ol><p><strong>缺点:</strong></p><ol><li><p>不支持<code>keys *</code>.</p></li><li><p>不支持<code>watch</code> ,<code>multi</code>事务.</p></li></ol><h3 id="predixy代理"><a class="header-anchor" href="#predixy代理">☀</a>predixy代理</h3><p>github地址：<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/joyieldInc/predixy">https://github.com/joyieldInc/predixy</a></p><p>predixy评测：<a target="_blank" rel="noopener external nofollow noreferrer" href="https://blog.csdn.net/rebaic/article/details/76384028">https://blog.csdn.net/rebaic/article/details/76384028</a></p><blockquote><p>特点:</p><ol><li>可以监控一套主从复制,也可以监控多套主从复制.</li><li>可以支持事务,但是只支持单group.</li></ol></blockquote><p>简易搭建步骤:</p><h4 id="下载"><a class="header-anchor" href="#下载">☀</a>下载</h4><p>地址:<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/joyieldInc/predixy/releases">https://github.com/joyieldInc/predixy/releases</a></p><p>​ 解压后,修改conf目录下的predixy.conf配置文件:</p><p>​ 解开端口号:<br> ​ <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420154626.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420154626.png" srcset="" alt="image-20210420154626000"></p><h4 id="哨兵sentinel代理配置"><a class="header-anchor" href="#哨兵sentinel代理配置">☀</a>哨兵sentinel代理配置:</h4><p>通过哨兵发现并代理Redis.</p><p><strong>1.引入哨兵配置:</strong></p><p>只能引入一种:<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420185046.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420185046.png" srcset="" alt="image-20210420185046763"></p><p><strong>2.配置主从哨兵:</strong></p><p>sentinel.conf<br> Sentinels中配置哨兵的集群.</p><p>❀两套主从:<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420171730.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420171730.png" srcset="" alt="image-20210420171730557"></p><p>Group为哨兵监控的主从组.名称为,哨兵配置文件中的主从关系名称.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420172020.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420172020.png" srcset="" alt="image-20210420172020529"></p><p>缺点是不支持事务.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420190508.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420190508.png" srcset="" alt="image-20210420190507982"></p><p>❀:一套主从<br> 只需要设置一个group就可以了.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420190427.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420190427.png" srcset="" alt="image-20210420190427258"></p><p>优点,支持事务<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420190456.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420190456.png" srcset="" alt="image-20210420190456542"></p><p><strong>3.启动predixy:</strong></p><p>进入bin目录:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./predixy ../conf/predixy.conf  <span class="comment">#执行脚本,指定对应的配置文件.</span></span><br></pre></td></tr></table></figure><h3 id="Cluster集群"><a class="header-anchor" href="#Cluster集群">☀</a>Cluster集群</h3><p>教程:<a target="_blank" rel="noopener external nofollow noreferrer" href="http://redis.cn/topics/cluster-tutorial.html">http://redis.cn/topics/cluster-tutorial.html</a><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/19/20210419200426.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/19/20210419200426.png" srcset="" alt="image-20210419200426813"></p><blockquote><p><strong>Redis 集群的数据分片</strong></p><p>Redis 集群没有使用一致性hash, 而是引入了 <strong>哈希槽</strong>的概念.</p><p>Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:</p><ul><li>节点 A 包含 0 到 5500号哈希槽.</li><li>节点 B 包含5501 到 11000 号哈希槽.</li><li>节点 C 包含11001 到 16384号哈希槽.</li></ul><p>这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.</p></blockquote><h4 id="单机创建集群演示"><a class="header-anchor" href="#单机创建集群演示">☀</a>单机创建集群演示</h4><p>create-cluster：</p><p>脚本目录:<code>/usr/local/redis/redis-5.0.12/utils/create-cluster</code></p><ol><li><p>修改create-cluster脚本中的配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Settings</span></span><br><span class="line">PORT=30000</span><br><span class="line">TIMEOUT=2000</span><br><span class="line">NODES=6			<span class="comment">#总结点数。</span></span><br><span class="line">REPLICAS=1		<span class="comment">#每个分片的从节点数。   分片数=(6/1+1)</span></span><br></pre></td></tr></table></figure></li><li><p>启动所有节点实例</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./create-cluster start</span><br></pre></td></tr></table></figure><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420193747.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420193747.png" srcset="" alt="image-20210420193747786"></p></li><li><p>创建分片和主从关系。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./create-cluster create</span><br></pre></td></tr></table></figure><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420193904.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420193904.png" srcset="" alt="image-20210420193904213"></p></li><li><p>链接集群：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli -c -p 30001</span><br></pre></td></tr></table></figure></li><li><p>停止集群：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./create-cluster stop</span><br></pre></td></tr></table></figure></li><li><p>清除日志，文件数据等缓存</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./create-cluster clean</span><br></pre></td></tr></table></figure><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420195653.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420195653.png" srcset="" alt="image-20210420195653783"></p></li></ol><h4 id="分布式创建集群演示"><a class="header-anchor" href="#分布式创建集群演示">☀</a>分布式创建集群演示</h4><p>使用客户端命令<code>redis-cli</code>创建.</p><p>查询帮助：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli --cluster <span class="built_in">help</span></span><br></pre></td></tr></table></figure><ol><li><p>启动所有redis实例</p><p>集群的redis配置文件添加：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">port 7000</span><br><span class="line">cluster-enabled yes</span><br><span class="line">cluster-config-file nodes.conf</span><br><span class="line">cluster-node-timeout 5000</span><br><span class="line">appendonly yes</span><br></pre></td></tr></table></figure></li><li><p>客户端命令，创建集群分片和主从关系：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli --cluster create  127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1</span><br></pre></td></tr></table></figure></li><li><p>迁移数据<br> 解决新增、减少节点的问题，解决数据倾斜的问题。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli --cluster reshard 127.0.0.1:30001         <span class="comment">#迁移节点上的数据。127.0.0.1:30001表示连接的集群地址，不代表要迁移的节点。</span></span><br></pre></td></tr></table></figure></li></ol><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420203647.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420203647.png" srcset="" alt="image-20210420203647629"></p><ol start="4"><li><p>查看集群节点信息</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli --cluster info 127.0.0.1:30001</span><br></pre></td></tr></table></figure></li></ol><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420204029.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420204029.png" srcset="" alt="image-20210420204029891"></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli --cluster check 127.0.0.1:30001</span><br></pre></td></tr></table></figure><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420203910.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420203910.png" srcset="" alt="image-20210420203910525"></p><h4 id="事务的使用技巧"><a class="header-anchor" href="#事务的使用技巧">☀</a>事务的使用技巧</h4><blockquote><p>分布式同一个事务中的不同key操作，如果计算出key的槽在不同节点，那么事务就无法提交。<img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420195341.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420195341.png" srcset="" alt="image-20210420195341221"></p><p><strong>解决方法：将事务关联的key，都设置{oo}类似一样的前缀，那么计算出的槽位，会在相同的节点下，这时候事务就可以提交执行。</strong><br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420195104.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210420195104.png" srcset="" alt="image-20210420195104679"></p></blockquote><h2 id="击穿"><a class="header-anchor" href="#击穿">☀</a>击穿</h2><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421080125.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421080125.png" srcset="" alt="image-20210421080118885"></p><blockquote><p>击穿发生场景：某个的key已经过期，在高并发情况下访问。造成直接到达访问数据库。<strong>通俗理解为.某个key有几千的请求。</strong></p></blockquote><blockquote><p><strong>❀击穿解决方案：</strong></p><p>当客户端访问key为null的时候，使用setnx锁。只有获取到锁的客户端才能访问数据库。其他客户端没有得到锁的情况下不能访问数据库，只能停留等待，设置睡眠时间重复获取。</p><p>如果设置了锁，可能会导致死锁。</p><p>​ 死锁解决方案：为锁设定过期时间。</p><p>如果设置了过期时间，可能会导致锁超时。</p><p>​ 锁超时解决方案：锁超时意味着，访问数据库时间超过了锁的过期时间。这时候可以加一个监控线程，监控数据是否被取回来了，如果没有，更新锁的过期时间。</p></blockquote><p>个人理解：做锁在一定程度上，降低了执行速度，但是提高了数据准确性。同时也可以间接降低服务访问压力。</p><h2 id="穿透"><a class="header-anchor" href="#穿透">☀</a>穿透</h2><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421083645.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421083645.png" srcset="" alt="image-20210421083645029"></p><blockquote><p>穿透出现的场景：客户端访问系统中根本不存在的数据。</p></blockquote><blockquote><p><strong>❀解决方案：</strong></p><p>使用布隆过滤器：</p><ol><li>client包含布隆算法和数据。</li><li>client包含了算法，数据在redis的bitmap中。redis无状态。</li><li>redis集成了布隆算法和数据（布隆过滤器）。</li></ol><p>布隆过滤器问题：不能删除数据。解决方法：1.使用布谷鸟。2.设置空key。</p></blockquote><h2 id="雪崩"><a class="header-anchor" href="#雪崩">☀</a>雪崩</h2><blockquote><p>大量的key同时失效，通俗理解为几百个key同时失效，每个可以有几十个几百个请求。</p></blockquote><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421094541.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421094541.png" srcset="" alt="image-20210421094541365"></p><p><strong>❀解决方案：</strong></p><blockquote><p>1.对于与时点性无关的数据，设置key的随机过期时间。</p><p>2.对与时点性有关的数据，比如零点必须统一过期的数据，采取强依赖的击穿方案。并且可以在业务层加入判断，零点延时，减缓访问压力。</p></blockquote><h2 id="分布式锁"><a class="header-anchor" href="#分布式锁">☀</a>分布式锁</h2><p><strong>setnx</strong></p><p>已经做锁了，对于效率要求就不大了。对于准确度和一致性要求高。</p><p><img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421100222.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421100222.png" srcset="" alt="image-20210421100222819"></p><p><strong>redisson</strong></p><p>锁的一种技术。</p><p><strong>zookeeper</strong></p><p>zookeeper没有redis快。数据可靠性、准确。zookeeper做分布式锁比较好。</p><h2 id="API"><a class="header-anchor" href="#API">☀</a>API</h2><p>官方文档：</p><p><a target="_blank" rel="noopener external nofollow noreferrer" href="https://docs.spring.io/spring-data/redis/docs/2.5.0/reference/html/#reference">https://docs.spring.io/spring-data/redis/docs/2.5.0/reference/html/#reference</a></p><h3 id="高阶API"><a class="header-anchor" href="#高阶API">☀</a>高阶API</h3><p><strong>RedisTemplate</strong></p><p>存放key的时候，会对key进行java字符串序列化.<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421193109.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421193109.png" srcset="" alt="image-20210421193109862"></p><p><strong>StringRedisTemplate</strong></p><p>存放key字符串就不会进行java字符串序列化操作了。<br> <img src="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421193130.png" class="lazyload" data-srcset="https://yefeit-note-picbed.oss-cn-shanghai.aliyuncs.com/blog/2021/04/20/20210421193130.png" srcset="" alt="image-20210421193130329"></p><p>代码示例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.msb.spring.redis.demo;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.RedisTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.StringRedisTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Auther</span>: tang</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2021/4/21 - 04 - 21 - 17:10</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: com.msb.spring.redis.demo</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span>: 1.0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestRedis</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    RedisTemplate redisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    StringRedisTemplate stringRedisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testRedis</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        stringRedisTemplate.opsForValue().set(<span class="string">&quot;k2&quot;</span>, <span class="string">&quot;321&quot;</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(stringRedisTemplate.opsForValue().get(<span class="string">&quot;k2&quot;</span>));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>springboot配置文件：</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring.redis.host</span>=<span class="string">192.168.57.129</span></span><br><span class="line"><span class="attr">spring.redis.port</span>=<span class="string">6379</span></span><br></pre></td></tr></table></figure><p>springboot启动测试类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DemoredisApplication</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> SpringApplication.run(DemoredisApplication.class, args);</span><br><span class="line">        <span class="type">RedisTest</span> <span class="variable">bean</span> <span class="operator">=</span> ctx.getBean(RedisTest.class);</span><br><span class="line">        bean.hashTest2();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="低阶API"><a class="header-anchor" href="#低阶API">☀</a>低阶API</h3><p><strong>RedisConnection</strong></p><p>示例代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">RedisConnection</span> <span class="variable">connection</span> <span class="operator">=</span> redisTemplate.getConnectionFactory().getConnection();</span><br><span class="line">connection.set(<span class="string">&quot;hello&quot;</span>.getBytes(), <span class="string">&quot;msbredis&quot;</span>.getBytes());</span><br><span class="line"><span class="type">byte</span>[] bytes = connection.get(<span class="string">&quot;hello&quot;</span>.getBytes());</span><br><span class="line">System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(bytes));</span><br></pre></td></tr></table></figure><h3 id="Hash实现"><a class="header-anchor" href="#Hash实现">☀</a>Hash实现</h3><h4 id="设置单个属性："><a class="header-anchor" href="#设置单个属性：">☀</a>设置单个属性：</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hashTest</span><span class="params">()</span> &#123;</span><br><span class="line">    HashOperations&lt;String, Object, Object&gt; hash = stringRedisTemplate.opsForHash();</span><br><span class="line">    hash.put(<span class="string">&quot;tang&quot;</span>, <span class="string">&quot;name&quot;</span>, <span class="string">&quot;汤晔飞&quot;</span>);</span><br><span class="line">    hash.put(<span class="string">&quot;tang&quot;</span>, <span class="string">&quot;age&quot;</span>, <span class="string">&quot;18&quot;</span>);</span><br><span class="line">    hash.entries(<span class="string">&quot;tang&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="存java对象。"><a class="header-anchor" href="#存java对象。">☀</a>存java对象。</h4><p>引入pom工具：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-json<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>实体类对象：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span>  String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span>  Integer age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(Integer age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Person&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>实现</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line">ObjectMapper objectMapper; <span class="comment">//对象属性转换工具。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hashTest2</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">    person.setName(<span class="string">&quot;小万&quot;</span>);</span><br><span class="line">    person.setAge(<span class="number">20</span>);</span><br><span class="line">    <span class="type">Jackson2HashMapper</span> <span class="variable">jm</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Jackson2HashMapper</span>(objectMapper, <span class="literal">false</span>);<span class="comment">//将对象转为map的工具，应该是先将对象转为json，再将json转为map。</span></span><br><span class="line">    stringRedisTemplate.setHashValueSerializer(<span class="keyword">new</span> <span class="title class_">Jackson2JsonRedisSerializer</span>&lt;Object&gt;(Object.class));<span class="comment">//由于stringRedisTemplate，支持的是string类型的序列化器。所以需要自定义hash的value序列化器使其支持int。</span></span><br><span class="line">    stringRedisTemplate.opsForHash().putAll(<span class="string">&quot;wan&quot;</span>, jm.toHash(person));</span><br><span class="line">    Map&lt;Object, Object&gt; wan = stringRedisTemplate.opsForHash().entries(<span class="string">&quot;wan&quot;</span>);</span><br><span class="line">    <span class="type">Person</span> <span class="variable">lordwan</span> <span class="operator">=</span> objectMapper.convertValue(wan, Person.class);</span><br><span class="line">    System.out.println(lordwan);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="自定义Template"><a class="header-anchor" href="#自定义Template">☀</a>自定义Template</h4><p>自定义的Bean生成。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Mytemplate</span> &#123;</span><br><span class="line">    <span class="comment">//干预未来容器里自己想要的模板。</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> StringRedisTemplate <span class="title function_">ooxx</span><span class="params">(RedisConnectionFactory fc)</span>&#123;</span><br><span class="line">        <span class="comment">//自己new时候需要传入，RedisConnectionFactory。</span></span><br><span class="line">        <span class="type">StringRedisTemplate</span> <span class="variable">st</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringRedisTemplate</span>(fc);</span><br><span class="line">        st.setHashValueSerializer(<span class="keyword">new</span> <span class="title class_">Jackson2JsonRedisSerializer</span>&lt;Object&gt;(Object.class));</span><br><span class="line">        <span class="keyword">return</span> st;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注入干预：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="meta">@Qualifier(&quot;ooxx&quot;)</span></span><br><span class="line">StringRedisTemplate stringRedisTemplate;</span><br></pre></td></tr></table></figure><h3 id="发布订阅实现"><a class="header-anchor" href="#发布订阅实现">☀</a>发布订阅实现</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pubandsubTest</span><span class="params">()</span> &#123;<span class="comment">/*往一个通道发送消息。*/</span></span><br><span class="line">    stringRedisTemplate.convertAndSend(<span class="string">&quot;ooxx&quot;</span>, <span class="string">&quot;hello&quot;</span>);</span><br><span class="line">    <span class="type">RedisConnection</span> <span class="variable">conn</span> <span class="operator">=</span> stringRedisTemplate.getConnectionFactory().getConnection();</span><br><span class="line">    conn.subscribe(<span class="keyword">new</span> <span class="title class_">MessageListener</span>() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onMessage</span><span class="params">(Message message, <span class="type">byte</span>[] bytes)</span> &#123;</span><br><span class="line">            System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody()));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;ooxx&quot;</span>.getBytes());</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        stringRedisTemplate.convertAndSend(<span class="string">&quot;ooxx&quot;</span>, <span class="string">&quot;汤小万&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="footer"><div class="copyright"><blockquote><p>博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</p><p>本文永久链接是：<a href="http://aner1001.gitee.io/posts/2021/07/10/10f923e8abfb/">http://aner1001.gitee.io/posts/2021/07/10/10f923e8abfb/</a></p></blockquote></div></div><div class="article-meta" id="bottom"><div class="new-meta-box"><div class="new-meta-item date" itemprop="dateUpdated" datetime="2021-07-10T21:33:53+08:00"><a class="notlink"><i class="fas fa-edit fa-fw" aria-hidden="true"></i><p>更新于：2021年7月10日</p></a></div><div class="new-meta-item meta-tags"><a class="tag" href="/tags/Redis/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>Redis</p></a></div><div class="new-meta-item share -mob-share-list"><div class="-mob-share-list share-body"> <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://aner1001.gitee.io/posts/2021/07/10/10f923e8abfb/&title=Redis学习笔记 - Tang's blog.&summary=计算机就是数据结构和算法."><img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png" srcset=""></a> <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener" target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=http://aner1001.gitee.io/posts/2021/07/10/10f923e8abfb/&title=Redis学习笔记 - Tang's blog.&summary=计算机就是数据结构和算法."><img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png" srcset=""></a> <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener" target="_blank" href="http://service.weibo.com/share/share.php?url=http://aner1001.gitee.io/posts/2021/07/10/10f923e8abfb/&title=Redis学习笔记 - Tang's blog.&summary=计算机就是数据结构和算法."><img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png" srcset=""></a><div class="hoverbox"> <a class="share"><img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/wechat.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/wechat.png" srcset=""></a><div class="target"> <img src=""></div></div> <a class="-mob-share-telegram" title="" rel="external nofollow noopener noreferrer noopener" target="_blank" href="https://t.me/share/url?url=http://aner1001.gitee.io/posts/2021/07/10/10f923e8abfb/&text=Redis学习笔记 - Tang's blog."><img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/telegram.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/telegram.png" srcset=""></a></div></div></div></div><div class="prev-next"><a class="prev" href="/posts/2021/07/28/f3e01b384e12/"><p class="title"><i class="fas fa-chevron-left" aria-hidden="true"></i>Windows系统重装</p><p class="content">记录重装Window10系统的过程，和使用工具。需要安装的软件备份。 ☀安装准备 ☀系统镜像 ☀下载 镜像下载地址：https://msdn.itellyou.cn/ 下载 64位 busin...</p></a><a class="next" href="/posts/2021/07/09/ce7f3937fcde/"><p class="title">Protostuff与Jedis工具类<i class="fas fa-chevron-right" aria-hidden="true"></i></p><p class="content">Redis有很多数据结构：string、hash、list、set等，但是在实际来发中，我们往往保存的是对象在redis中，所以键值对更加常用。 Redis在保存对象时，需要满足序列化，由于...</p></a></div></article><article class="post white-box reveal shadow" id="comments"><p ct><i class="fas fa-comments"></i> 评论</p><div id="valine_container" class="valine_thread"><i class="fas fa-cog fa-spin fa-fw fa-2x"></i></div></article></div><aside class="l_side"><section class="widget toc-wrapper shadow desktop mobile" id="toc-div"><header><i class="fas fa-list fa-fw" aria-hidden="true"></i> <span class="name">本文目录</span></header><div class="content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5"><span class="toc-text">基础概念</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8Redis"><span class="toc-text">为什么要使用Redis</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Redis%E7%AE%80%E4%BB%8B"><span class="toc-text">Redis简介</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%97%E7%AC%A6%E9%9B%86%E7%9A%84%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-text">字符集的的使用</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E5%AE%89%E8%A3%85"><span class="toc-text">Redis安装</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8D%B8%E8%BD%BDRedis"><span class="toc-text">卸载Redis</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4"><span class="toc-text">常用命令</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E7%9C%8BRedis"><span class="toc-text">查看Redis</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%99%BB%E5%BD%95Redis"><span class="toc-text">登录Redis</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%AF%E5%8A%A8Redis"><span class="toc-text">启动Redis</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%81%9C%E6%AD%A2Redis"><span class="toc-text">停止Redis</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E7%9C%8BRedis%E9%85%8D%E7%BD%AE%EF%BC%9A"><span class="toc-text">查看Redis配置：</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#conf%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6"><span class="toc-text">conf配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%86%85%E6%A0%B8%E6%BC%94%E5%8F%98%E8%BF%87%E7%A8%8B"><span class="toc-text">内核演变过程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%BC%94%E5%8F%98"><span class="toc-text">演变</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Redis%E5%86%85%E6%A0%B8"><span class="toc-text">Redis内核</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Value%E7%BB%93%E6%9E%84%E5%92%8C%E6%93%8D%E4%BD%9C%E5%91%BD%E4%BB%A4%EF%BC%9A"><span class="toc-text">Value结构和操作命令：</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%97%E7%AC%A6%E4%B8%B2"><span class="toc-text">字符串</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E5%80%BC"><span class="toc-text">数值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#bitmap"><span class="toc-text">bitmap</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-text">使用场景</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#List"><span class="toc-text">List</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Hash"><span class="toc-text">Hash</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Set"><span class="toc-text">Set</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#sorted-set"><span class="toc-text">sorted_set</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%AE%A1%E9%81%93pipeline%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-text">管道pipeline的使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85"><span class="toc-text">发布订阅</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BA%8B%E5%8A%A1%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-text">事务的使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Module%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-text">Module布隆过滤器的使用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85bloom-filter"><span class="toc-text">安装bloom-filter</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%91%BD%E4%BB%A4"><span class="toc-text">命令</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E7%A9%BF%E9%80%8F%E9%97%AE%E9%A2%98"><span class="toc-text">解决穿透问题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%87%A0%E7%A7%8D%E5%88%86%E5%B8%83%E5%BC%8F%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F"><span class="toc-text">几种分布式实现方式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%87%E6%BB%A4%E5%99%A8%E7%A7%8D%E7%B1%BB%E4%BB%8B%E7%BB%8D"><span class="toc-text">过滤器种类介绍</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A9%BF%E9%80%8F%E4%BA%86%EF%BC%8C%E4%BD%86%E6%98%AF%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8D%E5%AD%98%E5%9C%A8%E7%9A%84%E5%A4%84%E7%90%86"><span class="toc-text">穿透了，但是数据库不存在的处理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E4%BD%9C%E4%B8%BA%E7%BC%93%E5%AD%98"><span class="toc-text">Redis作为缓存</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%9E%E6%94%B6%E7%AD%96%E7%95%A5"><span class="toc-text">回收策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%87%E6%9C%9F%E6%B7%98%E6%B1%B0%E5%8E%9F%E7%90%86"><span class="toc-text">过期淘汰原理:</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8C%81%E4%B9%85%E5%8C%96"><span class="toc-text">持久化</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#RDB"><span class="toc-text">RDB</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#RDB%E6%8C%81%E4%B9%85%E5%8C%96%E5%8F%AF%E5%AE%9E%E7%8E%B0%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F"><span class="toc-text">RDB持久化可实现的几种方式:</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%98%BB%E5%A1%9E%EF%BC%8C%E6%95%B0%E6%8D%AE%E6%8B%B7%E8%B4%9D"><span class="toc-text">阻塞，数据拷贝:</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%9D%9E%E9%98%BB%E5%A1%9E%EF%BC%8C%E6%95%B0%E6%8D%AE%E6%8B%B7%E8%B4%9D"><span class="toc-text">非阻塞，数据拷贝:</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%9D%9E%E9%98%BB%E5%A1%9E-fork"><span class="toc-text">非阻塞,fork</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BC%98%E7%BC%BA%E7%82%B9%EF%BC%9A"><span class="toc-text">优缺点：</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%F0%9F%98%9C%E4%BC%98%E7%82%B9%EF%BC%9A"><span class="toc-text">😜优点：</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%F0%9F%A4%94%E7%BC%BA%E7%82%B9%EF%BC%9A"><span class="toc-text">🤔缺点：</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#fork%E4%BB%8B%E7%BB%8D"><span class="toc-text">fork介绍</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%BF%9B%E7%A8%8B%E7%9A%84%E6%A6%82%E5%BF%B5"><span class="toc-text">进程的概念</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#RDB%E9%85%8D%E7%BD%AE%EF%BC%9A"><span class="toc-text">RDB配置：</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AOF"><span class="toc-text">AOF</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#AOF%E6%A6%82%E5%BF%B5%EF%BC%9A"><span class="toc-text">AOF概念：</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BC%98%E7%82%B9%EF%BC%9A"><span class="toc-text">优点：</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOF%E5%8F%91%E5%B1%95%E5%8F%B2"><span class="toc-text">AOF发展史</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#4-0%E4%BB%A5%E5%89%8D"><span class="toc-text">4.0以前</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#4-0%E4%BB%A5%E5%90%8E"><span class="toc-text">4.0以后</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOF%E9%85%8D%E7%BD%AE%EF%BC%9A"><span class="toc-text">AOF配置：</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9E%E6%93%8D"><span class="toc-text">实操</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AFK%E5%8E%9F%E7%90%86"><span class="toc-text">AFK原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E6%9C%BA%E5%AD%98%E5%9C%A8%E7%9A%84%E9%97%AE%E9%A2%98"><span class="toc-text">单机存在的问题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AFK%E7%AD%96%E7%95%A5%E5%9B%BE%E8%A7%A3image-20210409165209783"><span class="toc-text">AFK策略图解</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AFK%E4%B8%80%E5%8F%98%E5%A4%9A%EF%BC%8C%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%EF%BC%8C%E9%92%88%E5%AF%B9x%E8%BD%B4%E3%80%82"><span class="toc-text">AFK一变多，数据一致性解决方案，针对x轴。</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BB%E5%A4%87%E3%80%81%E4%B8%BB%E4%BB%8E%E3%80%81%E4%B8%BB%E4%B8%BB%E6%A6%82%E5%BF%B5%E5%8C%BA%E5%88%86%EF%BC%9A"><span class="toc-text">主备、主从、主主概念区分：</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BB%E5%A4%87"><span class="toc-text">主备</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BB%E4%BB%8E"><span class="toc-text">主从</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BB%E4%B8%BB"><span class="toc-text">主主</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#CAP"><span class="toc-text">CAP</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#CAP%E6%A6%82%E5%BF%B5"><span class="toc-text">CAP概念</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%80%E3%80%81%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%9A%84%E4%B8%89%E4%B8%AA%E6%8C%87%E6%A0%87"><span class="toc-text">一、分布式系统的三个指标</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%8C%E3%80%81Partition-tolerance"><span class="toc-text">二、Partition tolerance</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%89%E3%80%81Consistency"><span class="toc-text">三、Consistency</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9B%9B%E3%80%81Availability"><span class="toc-text">四、Availability</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%94%E3%80%81Consistency-%E5%92%8C-Availability-%E7%9A%84%E7%9F%9B%E7%9B%BE"><span class="toc-text">五、Consistency 和 Availability 的矛盾</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Redis%E4%B8%AD%E4%B8%BB%E4%BB%8ECAP%E7%9B%91%E6%8E%A7%E3%80%82"><span class="toc-text">Redis中主从CAP监控。</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6"><span class="toc-text">Redis主从复制</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%80%E5%90%AF%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%EF%BC%9A"><span class="toc-text">开启主从复制：</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%91%BD%E4%BB%A4%E7%9A%84%E6%96%B9%E5%BC%8F%EF%BC%9A"><span class="toc-text">使用命令的方式：</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%80%9A%E8%BF%87%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E6%96%B9%E5%BC%8F%EF%BC%9A"><span class="toc-text">通过配置文件方式：</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%89%8B%E5%B7%A5%E5%88%87%E6%8D%A2%E4%B8%BB%E5%A4%87"><span class="toc-text">手工切换主备</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%9B%91%E6%8E%A7%E5%93%A8%E5%85%B5Sentinel"><span class="toc-text">监控哨兵Sentinel</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%90%AF%E5%8A%A8%E5%93%A8%E5%85%B5"><span class="toc-text">启动哨兵</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring%E6%95%B4%E5%90%88Redis"><span class="toc-text">Spring整合Redis</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E7%9A%84Sharing%E5%88%86%E7%89%87%E9%80%BB%E8%BE%91"><span class="toc-text">Redis的Sharing分片逻辑</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Y%E8%BD%B4%E6%8B%86%E5%88%86%E6%96%B9%E5%BC%8F"><span class="toc-text">Y轴拆分方式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Z%E8%BD%B4Sharding%E5%88%86%E7%89%87%E9%80%BB%E8%BE%91"><span class="toc-text">Z轴Sharding分片逻辑:</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E4%BB%A3%E7%90%86%E5%8E%9F%E7%90%86"><span class="toc-text">Redis代理原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9B%B4%E8%BF%9ERedis%E5%9C%BA%E6%99%AF"><span class="toc-text">客户端直连Redis场景:</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8Proxy%E4%BB%A3%E7%90%86%E9%93%BE%E6%8E%A5Redis%E5%9C%BA%E6%99%AF"><span class="toc-text">使用Proxy代理链接Redis场景:</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E8%99%9A%E6%8B%9F%E6%9C%8D%E5%8A%A1LVS-%E5%92%8Ckeepalived%E7%9B%91%E6%8E%A7-%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E4%BB%A3%E7%90%86Proxy"><span class="toc-text">使用虚拟服务LVS,和keepalived监控,负载均衡代理Proxy</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%A3%E7%90%86%E5%B1%82%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="toc-text">代理层的实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E4%B8%AD%E7%9A%84cluster%E5%88%86%E7%89%87%E6%8A%80%E6%9C%AF"><span class="toc-text">Redis中的cluster分片技术</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%A2%84%E5%88%86%E5%8C%BA%E6%8A%80%E6%9C%AF"><span class="toc-text">预分区技术</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA"><span class="toc-text">Redis集群搭建</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#twemproxy"><span class="toc-text">twemproxy</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#predixy%E4%BB%A3%E7%90%86"><span class="toc-text">predixy代理</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%8B%E8%BD%BD"><span class="toc-text">下载</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%93%A8%E5%85%B5sentinel%E4%BB%A3%E7%90%86%E9%85%8D%E7%BD%AE"><span class="toc-text">哨兵sentinel代理配置:</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Cluster%E9%9B%86%E7%BE%A4"><span class="toc-text">Cluster集群</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8D%95%E6%9C%BA%E5%88%9B%E5%BB%BA%E9%9B%86%E7%BE%A4%E6%BC%94%E7%A4%BA"><span class="toc-text">单机创建集群演示</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E5%88%9B%E5%BB%BA%E9%9B%86%E7%BE%A4%E6%BC%94%E7%A4%BA"><span class="toc-text">分布式创建集群演示</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%8B%E5%8A%A1%E7%9A%84%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7"><span class="toc-text">事务的使用技巧</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%87%BB%E7%A9%BF"><span class="toc-text">击穿</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%A9%BF%E9%80%8F"><span class="toc-text">穿透</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%9B%AA%E5%B4%A9"><span class="toc-text">雪崩</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81"><span class="toc-text">分布式锁</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#API"><span class="toc-text">API</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%AB%98%E9%98%B6API"><span class="toc-text">高阶API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%8E%E9%98%B6API"><span class="toc-text">低阶API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Hash%E5%AE%9E%E7%8E%B0"><span class="toc-text">Hash实现</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%AE%BE%E7%BD%AE%E5%8D%95%E4%B8%AA%E5%B1%9E%E6%80%A7%EF%BC%9A"><span class="toc-text">设置单个属性：</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AD%98java%E5%AF%B9%E8%B1%A1%E3%80%82"><span class="toc-text">存java对象。</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89Template"><span class="toc-text">自定义Template</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E5%AE%9E%E7%8E%B0"><span class="toc-text">发布订阅实现</span></a></li></ol></li></ol></div></section></aside><script>window.pdata={},pdata.ispage=!0,pdata.postTitle="Redis学习笔记",pdata.commentPath="",pdata.commentPlaceholder="";var l_header=document.getElementById("l_header");l_header.classList.add("show");var cover_wrapper=document.querySelector(".cover-wrapper");cover_wrapper.id="none",cover_wrapper.style.display="none"</script></div><footer class="footer clearfix"><br><br><div class="aplayer-container"><meting-js theme="#1BCDFC" autoplay="false" volume="0.7" loop="all" order="list" fixed="false" list-max-height="320px" server="netease" type="playlist" id="5472211102" list-folded="true"></meting-js></div><br><div class="social-wrapper"><a href="/atom.xml" class="social fas fa-rss flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a><a href="mailto:584856024@qq.com" class="social fas fa-envelope flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a><a href="https://github.com/aner1001" class="social fab fa-github flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a><a href="https://music.163.com/#/user/home?id=1402271985" class="social fas fa-headphones-alt flat-btn" target="_blank" rel="external nofollow noopener noreferrer"></a></div><div><p>博客内容遵循 <a target="_blank" rel="noopener external nofollow noreferrer" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</a></p></div><div><p><span id="lc-sv">本站总访问量为<span id="number"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 次</span> <span id="lc-uv">访客数为<span id="number"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 人</span></p></div> 本站使用 <a href="https://github.com/volantis-x/hexo-theme-volantis/tree/4.3.1" rel="external nofollow noreferrer" target="_blank" class="codename">Volantis</a> 作为主题，总访问量为<span id="busuanzi_value_site_pv"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 次<div class="copyright"><p><a href="/">Copyright © 2021-2021 XXX</a></p></div></footer><a id="s-top" class="fas fa-arrow-up fa-fw" href="javascript:void(0)" rel="external nofollow noreferrer"></a></div></div><div><script>function VPjax(){function e(e,t){this.name=t||e.name,this.run=()=>{e()}}this.list=[],this.start=()=>{for(var e=0;e<this.list.length;e++)this.list[e].run()},this.push=(t,n)=>{var i=new e(t,n);this.list.push(i)}}function loadScript(e,t){setTimeout((function(){var n=document.getElementsByTagName("head")[0]||document.documentElement,i=document.createElement("script");i.setAttribute("type","text/javascript"),t&&(i.onload=t),i.setAttribute("src",e),n.appendChild(i)}))}window.volantis={},window.volantis.loadcss=document.getElementById("loadcss"),volantis.pjax={},volantis.pjax.method={complete:new VPjax,error:new VPjax,send:new VPjax},volantis.pjax={...volantis.pjax,push:volantis.pjax.method.complete.push,error:volantis.pjax.method.error.push,send:volantis.pjax.method.send.push};var loadCSS=function(e,t,n,i){var o,a=window.document,s=a.createElement("link");if(t)o=t;else{var r=(a.body||a.getElementsByTagName("head")[0]).childNodes;o=r[r.length-1]}var d=a.styleSheets;if(i)for(var l in i)i.hasOwnProperty(l)&&s.setAttribute(l,i[l]);s.rel="stylesheet",s.href=e,s.media="only x",function e(t){if(a.body)return t();setTimeout((function(){e(t)}))}((function(){o.parentNode.insertBefore(s,t?o:o.nextSibling)}));var u=function(e){for(var t=s.href,n=d.length;n--;)if(d[n].href===t)return e();setTimeout((function(){u(e)}))};function h(){s.addEventListener&&s.removeEventListener("load",h),s.media=n||"all"}return s.addEventListener&&s.addEventListener("load",h),s.onloadcssdefined=u,u(h),s}</script><script>loadCSS("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.14/css/all.min.css",window.volantis.loadcss),loadCSS("https://cdn.jsdelivr.net/gh/l-lin/font-awesome-animation/dist/font-awesome-animation.min.css",window.volantis.loadcss),loadCSS("https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.css",window.volantis.loadcss)</script><script src="https://cdn.jsdelivr.net/npm/jquery@3.5/dist/jquery.min.js"></script><script>function pjax_fancybox(){$(".md .gallery").find("img").each((function(){var a=document.createElement("a");$(a).attr("class","fancybox"),$(a).attr("pjax-fancybox",""),$(a).attr("href",$(this).attr("src")),$(this).attr("data-original")&&$(a).attr("href",$(this).attr("data-original")),$(a).attr("data-fancybox","images");var t="";$(this).attr("alt")&&($(a).attr("data-caption",$(this).attr("alt")),t=$(this).attr("alt"));var n=document.createElement("div");$(n).addClass("fancybox"),$(this).wrap(n);var o=document.createElement("span");$(o).addClass("image-caption"),$(o).text(t),$(this).after(o),$(this).wrap(a)})),$(".md .gallery").find("img").fancybox({selector:'[data-fancybox="images"]',hash:!1,loop:!1,closeClick:!0,helpers:{overlay:{closeClick:!0}},buttons:["zoom","close"]})}function SCload_fancybox(){0!=$(".md .gallery").find("img").length&&(loadCSS("https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css",document.getElementById("loadcss")),loadScript("https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js",pjax_fancybox))}function Pjax_SCload_fancybox(){void 0===$.fancybox?SCload_fancybox():pjax_fancybox()}$((function(){SCload_fancybox()})),volantis.pjax.push(Pjax_SCload_fancybox),volantis.pjax.send(()=>{void 0!==$.fancybox&&$.fancybox.close()},"fancybox")</script><script src="https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js"></script><script type="text/javascript">var imgs=["https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/001.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/002.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/003.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/004.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/005.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/006.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/012.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/016.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/019.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/025.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/033.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/034.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/035.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/038.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/039.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/042.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/046.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/051.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/052.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/054.jpg","https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/056.jpg"];{function shuffle(t){for(var n=t.length;n--;){var i=Math.floor(Math.random()*n),l=t[i];t[i]=t[n],t[n]=l}}shuffle(imgs)}function Pjax_backstretch(){$("#cover-backstretch").backstretch(imgs,{duration:"10000",fade:"1500"})}loadScript("https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js",Pjax_backstretch)</script><div id="rightmenu-wrapper"><ul class="list-v rightmenu" id="rightmenu-content"><li class="option"><a class="vlts-menu opt fix-cursor-default" id="menu-copy-text" onclick='document.execCommand("copy")'><i class="fa fa-copy fa-fw"></i> 复制文本</a><hr id="hr-text"><a class="vlts-menu opt fix-cursor-default" id="menu-copy-href"><i class="fa fa-link fa-fw"></i> 复制链接</a><a class="vlts-menu opt fix-cursor-default" id="menu-open-href"><i class="fa fa-external-link-square-alt fa-fw"></i> 在新标签页打开</a><hr id="hr-href"><a class="vlts-menu opt fix-cursor-default" id="menu-copy-src"><i class="fa fa-image fa-fw"></i> 复制图片地址</a><hr id="hr-src"></li><li class="navigation"><a class="nav icon-only fix-cursor-default" onclick="history.back()"><i class="fa fa-arrow-left fa-fw"></i></a><a class="nav icon-only fix-cursor-default" onclick="history.forward()"><i class="fa fa-arrow-right fa-fw"></i></a><a class="nav icon-only fix-cursor-default" onclick="window.location.reload()"><i class="fa fa-redo fa-fw"></i></a><a class="nav icon-only fix-cursor-default" href="/"><i class="fa fa-home fa-fw"></i></a></li><hr><li><a class="vlts-menu fix-cursor-default" href="https://volantis.js.org/faqs/" id="https:volantisjsorgfaqs"><i class="fa fa-question fa-fw"></i> 常见问题</a></li><li><a class="vlts-menu fix-cursor-default" href="https://volantis.js.org/examples/" id="https:volantisjsorgexamples"><i class="fa fa-rss fa-fw"></i> 示例博客</a></li><li><a class="vlts-menu fix-cursor-default" href="https://volantis.js.org/contributors/" id="https:volantisjsorgcontributors"><i class="fa fa-fan fa-spin fa-fw"></i> 加入社区</a></li><hr><li><a class="vlts-menu fix-cursor-default" href="https://github.com/volantis-x/volantis-docs/" id="https:githubcomvolantis-xvolantis-docs"><i class="fa fa-code-branch fa-fw"></i> 本站源码</a></li><li><a class="vlts-menu fix-cursor-default" href="https://github.com/volantis-x/hexo-theme-volantis/" id="https:githubcomvolantis-xhexo-theme-volantis"><i class="fa fa-code-branch fa-fw"></i> 主题源码</a></li><hr><li><a class="vlts-menu fix-cursor-default" onclick='document.execCommand("print")'><i class="fa fa-print fa-fw"></i> 打印页面</a></li><hr><li><a class="vlts-menu fix-cursor-default toggle-mode-btn"><i class="fas fa-moon fa-fw"></i> Dark</a></li><hr><li class="music name"><p class="nav music-title fix-cursor-default"></p></li><li class="music ctrl"><a class="nav icon-only backward fix-cursor-default" onclick="aplayerBackward()"><i class="fa fa-step-backward fa-fw"></i></a><a class="nav icon-only toggle fix-cursor-default" onclick="aplayerToggle()"><i class="fa fa-play fa-fw"></i></a><a class="nav icon-only forward fix-cursor-default" onclick="aplayerForward()"><i class="fa fa-step-forward fa-fw"></i></a></li><li class="music volume"><a class="nav volume"><div class="aplayer-volume-bar-wrap"><div class="aplayer-volume-bar fix-cursor-pointer"><div class="aplayer-volume"></div><i class="left fa fa-volume-off fa-fw"></i><i class="right fa fa-volume-up fa-fw"></i></div></div></a></li></ul></div><script>function popMenu(e){var t=document.getElementById("rightmenu-wrapper"),n=document.getElementById("rightmenu-content"),l=document.documentElement.clientWidth||document.body.clientWidth,o=document.documentElement.clientHeight||document.body.clientHeight;t.style.left=e.clientX+"px",t.style.top=e.clientY+"px",t.style.display="block",2*e.clientX>l?n.classList.add("left"):n.classList.remove("left"),2*e.clientY>o?n.classList.add("top"):n.classList.remove("top");let c=document.getElementById("hr-text"),d=document.getElementById("hr-src"),y=document.getElementById("hr-href"),s=document.getElementById("menu-copy-src");null!=s&&(e.target.currentSrc?(s.style.display="block",s.addEventListener("click",(function(t){copyString(e.target.currentSrc)}),{once:!0}),d.style.display="block"):(s.style.display="none",d.style.display="none"));let r="";if(e.path)for(i=0;i<e.path.length;i++)null!=e.path[i].href&&e.path[i].href.length>0&&(r=e.path[i].href);let u=document.getElementById("menu-copy-text");u.style.display="none",c.style.display="none",0==r.length&&window.getSelection().toString()&&(u.style.display="block",c.style.display="block");let m=document.getElementById("menu-open-href");null!=m&&(r.length>0?(m.style.display="block",m.addEventListener("click",(function(e){window.open(r)}),{once:!0}),y.style.display="block"):(m.style.display="none",y.style.display="none"));let a=document.getElementById("menu-copy-href");return null!=a&&(r.length>0?(a.style.display="block",a.addEventListener("click",(function(e){copyString(r)}),{once:!0})):a.style.display="none"),volantis.APlayerLoaded&&checkAPlayer(),!1}function hideMenu(){document.getElementById("rightmenu-wrapper").style.display="none"}function copyString(e){const t=document.createElement("input");t.setAttribute("readonly","readonly"),document.body.appendChild(t),t.setAttribute("value",e),t.select(),document.execCommand("copy"),document.body.removeChild(t)}window.document.oncontextmenu=function(e){return!!e.ctrlKey||(!!/Android|webOS|BlackBerry/i.test(navigator.userAgent)||popMenu(e))},document.addEventListener("click",(function(e){document.getElementById("rightmenu-wrapper").style.display="none"})),document.execCommand("click")</script><script>function loadIssuesJS(){0!=$(".md").find(".issues-api").length&&loadScript("https://cdn.jsdelivr.net/npm/hexo-theme-volantis@4.3.1/source/js/issues.min.js")}$((function(){loadIssuesJS()})),volantis.pjax.push(()=>{"undefined"==typeof IssuesAPI&&loadIssuesJS()},"IssuesJS")</script><script defer="defer" src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.1.0/dist/lazyload.min.js"></script><script>window.lazyLoadOptions={elements_selector:".lazyload",threshold:0},window.addEventListener("LazyLoad::Initialized",(function(n){window.lazyLoadInstance=n.detail.instance}),!1),document.addEventListener("DOMContentLoaded",(function(){lazyLoadInstance.update()})),document.addEventListener("pjax:complete",(function(){lazyLoadInstance.update()}))</script><script>window.FPConfig={delay:0,ignoreKeywords:[],maxRPS:5,hoverDelay:25}</script><script defer="defer" src="https://cdn.jsdelivr.net/gh/gijo-varghese/flying-pages@2.1.2/flying-pages.min.js"></script><script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script><script>var clipboard=new ClipboardJS(".btn-copy",{target:function(e){return e.nextElementSibling}});function wait(e,i){window.setTimeout(e,i)}function pjax_initCopyCode(){if($(".highlight .code pre").length+$(".article pre code").length!=0){var e="";e+='<button class="btn-copy" data-clipboard-snippet="">',e+='<i class="fas fa-copy"></i><span>COPY</span>',e+="</button>",$(".highlight .code pre").before(e),$(".article pre code").before(e),clipboard.off("success").on("success",(function(e){let i=$(e.trigger);i.addClass("copied");let a=$(i.find("i"));a.removeClass("fa-copy"),a.addClass("fa-check-circle");let n=$(i.find("span"));n[0].innerText="COPIED",wait((function(){a.removeClass("fa-check-circle"),a.addClass("fa-copy"),n[0].innerText="COPY"}),2e3)})),clipboard.off("error").on("error",(function(e){e.clearSelection();let i=$(e.trigger);i.addClass("copy-failed");let a=$(i.find("i"));a.removeClass("fa-copy"),a.addClass("fa-times-circle");let n=$(i.find("span"));n[0].innerText="COPY FAILED",wait((function(){a.removeClass("fa-times-circle"),a.addClass("fa-copy"),n[0].innerText="COPY"}),2e3)}))}}$((function(){pjax_initCopyCode()})),volantis.pjax.push(pjax_initCopyCode)</script><script type="text/javascript">function pjax_scrollrebeal(){ScrollReveal().reveal(".l_main .reveal",{distance:"32px",duration:"800",interval:"20",scale:"1",easing:"ease-out"})}loadScript("https://cdn.jsdelivr.net/npm/scrollreveal@4.0.6/dist/scrollreveal.min.js"),$((function(){var e=setInterval((function(){"block"==$("#safearea").css("display")&&"undefined"!=typeof ScrollReveal&&(clearInterval(e),pjax_scrollrebeal())}),100)})),volantis.pjax.push(pjax_scrollrebeal)</script><script>let APlayerController=new Object;APlayerController.id="5472211102",APlayerController.volume="0.7",loadCSS("https://cdn.jsdelivr.net/npm/aplayer@1.10/dist/APlayer.min.css",window.volantis.loadcss),loadScript("https://cdn.jsdelivr.net/npm/aplayer@1.10/dist/APlayer.min.js"),window.volantis.APlayerLoaded=0;var checkAPlayer=setInterval((function(){window.APlayer&&"block"==$("#safearea").css("display")&&(clearInterval(checkAPlayer),window.volantis.APlayerLoaded||window.MetingJSElement||(window.volantis.APlayerLoaded=1,loadScript("https://cdn.jsdelivr.net/npm/meting@2.0/dist/Meting.min.js")))}),2500);loadScript("https://cdn.jsdelivr.net/npm/hexo-theme-volantis@4.3.1/source/js/aplayer.min.js")</script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-volantis@4.3.1/source/js/valine.min.js"></script><script>function emoji(i,e,a){return i+"/"+i+"-"+e+"."+a}for(var emojiMaps={},i=1;i<=54;i++)emojiMaps["tieba-"+i]=emoji("tieba",i,"png");for(i=1;i<=101;i++)emojiMaps["qq-"+i]=emoji("qq",i,"gif");for(i=1;i<=116;i++)emojiMaps["aru-"+i]=emoji("aru",i,"gif");for(i=1;i<=125;i++)emojiMaps["twemoji-"+i]=emoji("twemoji",i,"png");for(i=1;i<=4;i++)emojiMaps["weibo-"+i]=emoji("weibo",i,"png");function pjax_valine(){if(!document.querySelectorAll("#valine_container")[0])return;let i=pdata.commentPlaceholder||"快来评论吧~",e=pdata.commentPath;if(0==e.length){e=""||decodeURI(window.location.pathname)}(new Valine).init(Object.assign({path:null,placeholder:"快来评论吧~",appId:"eL9fCmXLl3nAe225XYo1cRxj-MdYXbMMI",appKey:"5uTd5Uu8WJJIej6Efrivsvaa",meta:["nick","mail","link"],requiredFields:["nick","mail"],enableQQ:!0,recordIP:!1,avatar:"robohash",pageSize:10,lang:"zh-cn",highlight:!0,mathJax:!1},{el:"#valine_container",path:e,placeholder:i,emojiCDN:"https://cdn.jsdelivr.net/gh/volantis-x/cdn-emoji/valine/",emojiMaps:emojiMaps}))}$((function(){pjax_valine()})),volantis.pjax.push(pjax_valine)</script><script defer="defer" src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js" data-pjax></script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-volantis@4.3.1/source/js/app.min.js"></script><script>const SearchServiceimagePath="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@master/img/",ROOT="/".endsWith("/")?"/":"//";function listenSearch(){customSearch=new HexoSearch({imagePath:SearchServiceimagePath})}function setSearchService(){listenSearch()}$(".input.u-search-input").one("focus",(function(){loadScript("https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@2.6.4/js/search.js",setSearchService)}))</script><script src="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.js"></script><script type="text/javascript">$((function(){Waves.attach(".flat-btn",["waves-button"]),Waves.attach(".float-btn",["waves-button","waves-float"]),Waves.attach(".float-btn-light",["waves-button","waves-float","waves-light"]),Waves.attach(".flat-box",["waves-block"]),Waves.attach(".float-box",["waves-block","waves-float"]),Waves.attach(".waves-image"),Waves.init()}))</script><script src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@2/js/comment_typing.js"></script><script defer="defer">const LCCounter={app_id:"C2dIVXJ48qC7eHgvQRId6niR-MdYXbMMI",app_key:"1956OGJ4LQhDxheBAO5WhPJn",custom_api_server:"",getRecord:(e,t,r)=>new Promise((function(n,i){e("get","/classes/Counter?where="+encodeURIComponent(JSON.stringify({url:t}))).then(e=>e.json()).then(({results:o,code:s,error:a})=>{if(401===s)throw a;if(o&&o.length>0){var l=o[0];n(l)}else e("post","/classes/Counter",{url:t,title:r,times:0}).then(e=>e.json()).then((e,t)=>{if(t)throw t;n(e)}).catch(e=>{console.error("Failed to create",e),i(e)})}).catch(e=>{console.error("LeanCloud Counter Error:",e),i(e)})})),increment:(e,t)=>new Promise((function(r,n){e("post","/batch",{requests:t}).then(e=>{if((e=e.json()).error)throw e.error;r(e)}).catch(e=>{console.error("Failed to save visitor count",e),n(e)})})),buildIncrement:e=>({method:"PUT",path:"/1.1/classes/Counter/"+e,body:{times:{__op:"Increment",amount:1}}}),validUV(){var e="LeanCloudUVTimestamp",t=localStorage.getItem(e);return!(t&&(new Date).getTime()-parseInt(t)<=864e5)&&(localStorage.setItem(e,(new Date).getTime().toString()),!0)},addCount(e){var t=[],r=[],n=document.querySelector("#lc-sv"),o=this.getRecord(e,"http://aner1001.gitee.io/#lc-sv","Visits").then(e=>{r.push(this.buildIncrement(e.objectId));var t=document.querySelectorAll("#lc-sv #number");t.length>0&&t.forEach((t,r,i)=>{t.innerText=e.times+1,n&&(n.style.display="inline")})});t.push(o);var s=document.querySelector("#lc-uv"),a=this.getRecord(e,"http://aner1001.gitee.io/#lc-uv","Visitors").then(e=>{var t=this.validUV();t&&r.push(this.buildIncrement(e.objectId));var n=document.querySelectorAll("#lc-uv #number");n.length>0&&n.forEach((r,n,i)=>{r.innerText=e.times+(t?1:0),s&&(s.style.display="inline")})});t.push(a);var l=document.querySelectorAll("#lc-pv");for(l.length,i=0;i<l.length;i++){let n=l[i],o=n.getAttribute("data-title");var h="http://aner1001.gitee.io"+n.getAttribute("data-path");if(h){var c=this.getRecord(e,h,o).then(e=>{let t=window.location.pathname;if(t.includes("index.html")&&(t=t.substring(0,t.lastIndexOf("index.html"))),n.getAttribute("data-path")==t&&r.push(this.buildIncrement(e.objectId)),n){var i=n.querySelector("#lc-pv #number");i&&(n.getAttribute("data-path")==t?i.innerText=(e.times||0)+1:i.innerText=e.times||0,n.style.display="inline")}});t.push(c)}}Promise.all(t).then(()=>{r.length>0&&this.increment(e,r)})},fetchData(e){this.addCount((t,r,n)=>fetch(`${e}/1.1${r}`,{method:t,headers:{"X-LC-Id":this.app_id,"X-LC-Key":this.app_key,"Content-Type":"application/json"},body:JSON.stringify(n)}))},refreshCounter(){var e="-MdYXbMMI"!==this.app_id.slice(-9)?this.custom_api_server:`https://${this.app_id.slice(0,8).toLowerCase()}.api.lncldglobal.com`;e?this.fetchData(e):fetch("https://app-router.leancloud.cn/2/route?appId="+this.app_id).then(e=>e.json()).then(({api_server:e})=>{this.fetchData("https://"+e)})}};LCCounter.refreshCounter(),document.addEventListener("pjax:complete",(function(){LCCounter.refreshCounter()}))</script><script>const rootElement=document.documentElement,darkModeStorageKey="user-color-scheme",rootElementDarkModeAttributeName="data-user-color-scheme",setLS=(e,t)=>{localStorage.setItem(e,t)},removeLS=e=>{localStorage.removeItem(e)},getLS=e=>localStorage.getItem(e),getModeFromCSSMediaQuery=()=>window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light",resetRootDarkModeAttributeAndLS=()=>{var e;rootElement.removeAttribute("data-user-color-scheme"),e=darkModeStorageKey,localStorage.removeItem(e)},validColorModeKeys={dark:!0,light:!0},applyCustomDarkModeSettings=e=>{const t=e||getLS(darkModeStorageKey);t===getModeFromCSSMediaQuery()?resetRootDarkModeAttributeAndLS():validColorModeKeys[t]?rootElement.setAttribute("data-user-color-scheme",t):resetRootDarkModeAttributeAndLS()},invertDarkModeObj={dark:"light",light:"dark"},toggleCustomDarkMode=()=>{let e=getLS(darkModeStorageKey);if(validColorModeKeys[e])e=invertDarkModeObj[e];else{if(null!==e)return;e=invertDarkModeObj[getModeFromCSSMediaQuery()]}var t,o;return t=darkModeStorageKey,o=e,localStorage.setItem(t,o),e};var btn=$("#wrapper .toggle-mode-btn,#rightmenu-wrapper .toggle-mode-btn");function bindToggleButton(){btn.on("click",e=>{const t=toggleCustomDarkMode();applyCustomDarkModeSettings(t)})}applyCustomDarkModeSettings(),document.addEventListener("DOMContentLoaded",bindToggleButton),volantis.pjax.push(bindToggleButton),volantis.pjax.send(()=>{btn.unbind("click")},"toggle-mode-btn-unbind")</script><script>function listennSidebarTOC(){const e=document.querySelectorAll(".toc li");if(!e.length)return;const t=[...e].map(e=>{const t=e.querySelector(".toc-link"),n=document.getElementById(decodeURI(t.getAttribute("href")).replace("#",""));return t.addEventListener("click",e=>{e.preventDefault(),window.scrollTo({top:n.offsetTop+100,behavior:"smooth"})}),n});!function n(o){o=Math.floor(o+1e4);let r=new IntersectionObserver((r,c)=>{let i=document.documentElement.scrollHeight+100;if(i>o)return c.disconnect(),void n(i);let l=function(e){let n=0,o=e[n];if(o.boundingClientRect.top>0)return n=t.indexOf(o.target),0===n?0:n-1;for(;n<e.length;n++){if(!(e[n].boundingClientRect.top<=0))return t.indexOf(o.target);o=e[n]}return t.indexOf(o.target)}(r);!function(e){if(e.classList.contains("active-current"))return;document.querySelectorAll(".toc .active").forEach(e=>{e.classList.remove("active","active-current")}),e.classList.add("active","active-current");let t=e.parentNode;for(;!t.matches(".toc");)t.matches("li")&&t.classList.add("active"),t=t.parentNode}(e[l])},{rootMargin:o+"px 0px -100% 0px",threshold:0});t.forEach(e=>{e&&r.observe(e)})}(document.documentElement.scrollHeight)}document.addEventListener("DOMContentLoaded",listennSidebarTOC),document.addEventListener("pjax:success",listennSidebarTOC)</script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/sw.js").then((function(e){e.onupdatefound=function(){var r=e.installing;r.onstatechange=function(){switch(r.state){case"installed":navigator.serviceWorker.controller?console.log("Updated serviceWorker."):console.log("serviceWorker Sucess!");break;case"redundant":console.log("The installing service worker became redundant.")}}}})).catch((function(e){console.log("Error during service worker registration:",e)}))</script><script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.min.js"></script><div class="pjax-animate"><script src="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js"></script><div id="loading-bar-wrapper"><script>NProgress.configure({parent:"#loading-bar-wrapper",trickleSpeed:100})</script></div><script>window.ShowLoading=function(){NProgress.start()},window.HideLoading=function(){NProgress.done()}</script><script>volantis.pjax.push(window.HideLoading,"HideLoading"),volantis.pjax.send(window.ShowLoading,"ShowLoading"),volantis.pjax.error(window.HideLoading,"HideLoading")</script></div><script>var pjax;document.addEventListener("DOMContentLoaded",(function(){pjax=new Pjax({elements:'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox])',selectors:["title","#pjax-container","#pjax-header-nav-list"],cacheBust:!1,timeout:5e3})})),document.addEventListener("pjax:send",(function(e){try{var n=window.location.pathname,t=e.triggerElement.href,o=[""];""!=o[0]&&o.forEach(e=>{-1==n.indexOf(e)&&-1==t.indexOf(e)||(window.location.href=t)})}catch(e){}window.subData=null,volantis.$switcher.removeClass("active"),volantis.$header.removeClass("z_search-open"),volantis.$wrapper.removeClass("sub"),volantis.$topBtn.unbind("click"),$(".menu a").unbind("click"),$(window).unbind("resize"),$(window).unbind("scroll"),$(document).unbind("scroll"),$(document).unbind("click"),$("body").unbind("click"),volantis.pjax.method.send.start()})),document.addEventListener("pjax:complete",(function(){$(".nav-main").find(".list-v").not(".menu-phone").removeAttr("style",""),$(".menu-phone.list-v").removeAttr("style",""),$("script[data-pjax], .pjax-reload script").each((function(){$(this).parent().append($(this).remove())}));try{volantis.pjax.method.complete.start()}catch(e){console.log(e)}})),document.addEventListener("pjax:error",(function(e){volantis.pjax.method.error.start(),window.location.href=e.triggerElement.href}))</script></div></body></html>